diff options
Diffstat (limited to 'sql')
241 files changed, 25679 insertions, 11792 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 4cdc4c01c4e..002aabb91b0 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -1,3 +1,18 @@ +# Copyright (C) 2006 MySQL 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 +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX -DUSE_SYMDIR") SET(CMAKE_C_FLAGS_DEBUG @@ -22,8 +37,7 @@ SET_SOURCE_FILES_PROPERTIES(${CMAKE_SOURCE_DIR}/sql/message.rc ${PROJECT_SOURCE_DIR}/include/sql_state.h PROPERTIES GENERATED 1) -ADD_DEFINITIONS(-DHAVE_ROW_BASED_REPLICATION -DMYSQL_SERVER - -D_CONSOLE -DHAVE_DLOPEN) +ADD_DEFINITIONS(-DMYSQL_SERVER -D_CONSOLE -DHAVE_DLOPEN) ADD_EXECUTABLE(mysqld ../sql-common/client.c derror.cc des_key_file.cc discover.cc ../libmysql/errmsg.c field.cc field_conv.cc @@ -33,7 +47,7 @@ ADD_EXECUTABLE(mysqld ../sql-common/client.c derror.cc des_key_file.cc hostname.cc init.cc item.cc item_buff.cc item_cmpfunc.cc item_create.cc item_func.cc item_geofunc.cc item_row.cc item_strfunc.cc item_subselect.cc item_sum.cc item_timefunc.cc - item_uniq.cc key.cc log.cc lock.cc log_event.cc message.rc + key.cc log.cc lock.cc log_event.cc message.rc message.h mf_iocache.cc my_decimal.cc ../sql-common/my_time.c mysqld.cc net_serv.cc nt_servc.cc nt_servc.h opt_range.cc opt_range.h opt_sum.cc @@ -54,7 +68,8 @@ ADD_EXECUTABLE(mysqld ../sql-common/client.c derror.cc des_key_file.cc event_queue.cc event_db_repository.cc sql_tablespace.cc events.cc ../sql-common/my_user.c partition_info.cc rpl_utility.cc rpl_injector.cc sql_locale.cc - rpl_rli.cc rpl_mi.cc + rpl_rli.cc rpl_mi.cc sql_servers.cc + sql_connect.cc scheduler.cc ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h ${PROJECT_SOURCE_DIR}/include/mysqld_error.h diff --git a/sql/Makefile.am b/sql/Makefile.am index edb45b3e54f..ac14ed3e883 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -1,9 +1,8 @@ -# Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +# Copyright (C) 2000-2006 MySQL 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 -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -30,19 +29,21 @@ libexec_PROGRAMS = mysqld EXTRA_PROGRAMS = gen_lex_hash bin_PROGRAMS = mysql_tzinfo_to_sql gen_lex_hash_LDFLAGS = @NOINST_LDFLAGS@ -LDADD = $(top_builddir)/vio/libvio.a \ +SUPPORTING_LIBS = $(top_builddir)/vio/libvio.a \ $(top_builddir)/mysys/libmysys.a \ $(top_builddir)/dbug/libdbug.a \ $(top_builddir)/regex/libregex.a \ - $(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@ -mysqld_DEPENDENCIES= @mysql_plugin_libs@ $(LDADD) + $(top_builddir)/strings/libmystrings.a +mysqld_DEPENDENCIES= @mysql_plugin_libs@ $(SUPPORTING_LIBS) +LDADD = $(SUPPORTING_LIBS) @ZLIB_LIBS@ mysqld_LDADD = @MYSQLD_EXTRA_LDFLAGS@ \ @pstack_libs@ \ @mysql_plugin_libs@ \ $(LDADD) $(CXXLDFLAGS) $(WRAPLIBS) @LIBDL@ \ - @yassl_libs@ @openssl_libs@ + $(yassl_libs) $(openssl_libs) + noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ - item_strfunc.h item_timefunc.h item_uniq.h \ + item_strfunc.h item_timefunc.h \ item_xmlfunc.h \ item_create.h item_subselect.h item_row.h \ mysql_priv.h item_geofunc.h sql_bitmap.h \ @@ -63,11 +64,12 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ tztime.h my_decimal.h\ sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h \ parse_file.h sql_view.h sql_trigger.h \ - sql_array.h sql_cursor.h events.h \ + sql_array.h sql_cursor.h events.h scheduler.h \ event_db_repository.h event_queue.h \ - sql_plugin.h authors.h sql_partition.h event_data_objects.h \ - partition_info.h partition_element.h event_scheduler.h \ - contributors.h + sql_plugin.h authors.h \ + event_data_objects.h event_scheduler.h \ + sql_partition.h partition_info.h partition_element.h \ + contributors.h sql_servers.h mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \ @@ -78,11 +80,12 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ lock.cc my_lock.c \ sql_string.cc sql_manager.cc sql_map.cc \ mysqld.cc password.c hash_filo.cc hostname.cc \ - set_var.cc sql_parse.cc sql_yacc.yy \ + sql_connect.cc scheduler.cc sql_parse.cc \ + set_var.cc sql_yacc.yy \ sql_base.cc table.cc sql_select.cc sql_insert.cc \ sql_prepare.cc sql_error.cc sql_locale.cc \ sql_update.cc sql_delete.cc uniques.cc sql_do.cc \ - procedure.cc item_uniq.cc sql_test.cc \ + procedure.cc sql_test.cc \ log.cc log_event.cc init.cc derror.cc sql_acl.cc \ unireg.cc des_key_file.cc \ discover.cc time.cc opt_range.cc opt_sum.cc \ @@ -105,7 +108,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ event_scheduler.cc event_data_objects.cc \ event_queue.cc event_db_repository.cc events.cc \ sql_plugin.cc sql_binlog.cc \ - sql_builtin.cc sql_tablespace.cc partition_info.cc + sql_builtin.cc sql_tablespace.cc partition_info.cc \ + sql_servers.cc gen_lex_hash_SOURCES = gen_lex_hash.cc @@ -120,12 +124,15 @@ DEFS = -DMYSQL_SERVER \ -DLIBDIR="\"$(MYSQLLIBdir)\"" \ @DEFS@ -BUILT_SOURCES = sql_yacc.cc sql_yacc.h lex_hash.h -EXTRA_DIST = udf_example.c udf_example.def $(BUILT_SOURCES) \ +BUILT_MAINT_SRC = sql_yacc.cc sql_yacc.h +BUILT_SOURCES = $(BUILT_MAINT_SRC) lex_hash.h +EXTRA_DIST = udf_example.c udf_example.def $(BUILT_MAINT_SRC) \ nt_servc.cc nt_servc.h message.mc CMakeLists.txt \ udf_example.c udf_example.def -CLEANFILES = lex_hash.h sql_yacc.cc sql_yacc.h sql_yacc.output -AM_YFLAGS = -d --debug --verbose +CLEANFILES = lex_hash.h sql_yacc.output +DISTCLEANFILES = $(EXTRA_PROGRAMS) +MAINTAINERCLEANFILES = $(BUILT_MAINT_SRC) +AM_YFLAGS = -d --verbose mysql_tzinfo_to_sql.cc: rm -f mysql_tzinfo_to_sql.cc @@ -146,23 +153,13 @@ link_sources: mysql_tzinfo_to_sql.cc mysql_tzinfo_to_sql.o: $(mysql_tzinfo_to_sql_SOURCES) $(CXXCOMPILE) -c $(INCLUDES) -DTZINFO2SQL $< -# Try to get better dependencies for the grammar. Othervise really bad -# things like different grammars for different pars of MySQL can -# happen if you are unlucky. -sql_yacc.cc: sql_yacc.yy -sql_yacc.h: sql_yacc.yy - -sql_yacc.o: sql_yacc.cc sql_yacc.h $(HEADERS) - @echo "Note: The following compile may take a long time." - @echo "If it fails, re-run configure with --with-low-memory" - $(CXXCOMPILE) $(LM_CFLAGS) -c $< - # This generates lex_hash.h # NOTE Built sources should depend on their sources not the tool # this avoid the rebuild of the built files in a source dist lex_hash.h: gen_lex_hash.cc lex.h $(MAKE) $(AM_MAKEFLAGS) gen_lex_hash$(EXEEXT) - ./gen_lex_hash$(EXEEXT) > $@ + ./gen_lex_hash$(EXEEXT) > $@-t + $(MV) $@-t $@ # the following three should eventually be moved out of this directory ha_ndbcluster.o:ha_ndbcluster.cc ha_ndbcluster.h diff --git a/sql/authors.h b/sql/authors.h index 980ec7670de..48b807c7884 100644 --- a/sql/authors.h +++ b/sql/authors.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2005 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2005-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -12,7 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* Structure of the name list */ diff --git a/sql/client_settings.h b/sql/client_settings.h index a8cd36af102..f0742cd8046 100644 --- a/sql/client_settings.h +++ b/sql/client_settings.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/contributors.h b/sql/contributors.h index dca232b9b69..87001e29d88 100644 --- a/sql/contributors.h +++ b/sql/contributors.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2005 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -12,7 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* Structure of the name list */ diff --git a/sql/custom_conf.h b/sql/custom_conf.h index 19ced12bfbb..137b7e9eef2 100644 --- a/sql/custom_conf.h +++ b/sql/custom_conf.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/derror.cc b/sql/derror.cc index bee818a14c1..0e74d411b1f 100644 --- a/sql/derror.cc +++ b/sql/derror.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2005 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/des_key_file.cc b/sql/des_key_file.cc index 77cb0c8de0f..d99d712b45a 100644 --- a/sql/des_key_file.cc +++ b/sql/des_key_file.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2001-2003, 2005 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/discover.cc b/sql/discover.cc index 938a05ff4a7..395bfbfff45 100644 --- a/sql/discover.cc +++ b/sql/discover.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2004 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 4b9aa43b14b..9999424ad60 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -21,6 +20,8 @@ #include "event_db_repository.h" #include "sp_head.h" +/* That's a provisional solution */ +extern Event_db_repository events_event_db_repository; #define EVEX_MAX_INTERVAL_VALUE 1000000000L @@ -31,6 +32,47 @@ event_change_security_context(THD *thd, LEX_STRING user, LEX_STRING host, static void event_restore_security_context(THD *thd, Security_context *backup); + +/* + Initiliazes dbname and name of an Event_queue_element_for_exec + object + + SYNOPSIS + Event_queue_element_for_exec::init() + + RETURN VALUE + FALSE OK + TRUE Error (OOM) +*/ + +bool +Event_queue_element_for_exec::init(LEX_STRING db, LEX_STRING n) +{ + if (!(dbname.str= my_strndup(db.str, dbname.length= db.length, MYF(MY_WME)))) + return TRUE; + if (!(name.str= my_strndup(n.str, name.length= n.length, MYF(MY_WME)))) + { + my_free((gptr) dbname.str, MYF(0)); + return TRUE; + } + return FALSE; +} + + +/* + Destructor + + SYNOPSIS + Event_queue_element_for_exec::~Event_queue_element_for_exec() +*/ + +Event_queue_element_for_exec::~Event_queue_element_for_exec() +{ + my_free((gptr) dbname.str, MYF(0)); + my_free((gptr) name.str, MYF(0)); +} + + /* Returns a new instance @@ -124,8 +166,8 @@ void Event_parse_data::init_body(THD *thd) { DBUG_ENTER("Event_parse_data::init_body"); - DBUG_PRINT("info", ("body=[%s] body_begin=0x%lx end=0x%lx", body_begin, - body_begin, thd->lex->ptr)); + DBUG_PRINT("info", ("body: '%s' body_begin: 0x%lx end: 0x%lx", body_begin, + (long) body_begin, (long) thd->lex->ptr)); body.length= thd->lex->ptr - body_begin; const uchar *body_end= body_begin + body.length - 1; @@ -395,23 +437,31 @@ Event_parse_data::init_starts(THD *thd) if ((not_used= item_starts->get_date(<ime, TIME_NO_ZERO_DATE))) goto wrong_value; - /* Let's check whether time is in the past */ + /* + Let's check whether time is in the past. + Note: This call is not post year 2038 safe. That's because + thd->query_start() is of time_t, while gmt_sec_to_TIME() + wants my_time_t. In the case time_t is larger than my_time_t + an overflow might happen and events subsystem will not work as + expected. + */ thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, (my_time_t) thd->query_start()); - DBUG_PRINT("info",("now =%lld", TIME_to_ulonglong_datetime(&time_tmp))); - DBUG_PRINT("info",("starts=%lld", TIME_to_ulonglong_datetime(<ime))); + DBUG_PRINT("info",("now: %ld starts: %ld", + (long) TIME_to_ulonglong_datetime(&time_tmp), + (long) TIME_to_ulonglong_datetime(<ime))); if (TIME_to_ulonglong_datetime(<ime) < TIME_to_ulonglong_datetime(&time_tmp)) goto wrong_value; /* - This may result in a 1970-01-01 date if ltime is > 2037-xx-xx. - CONVERT_TZ has similar problem. - mysql_priv.h currently lists + Again, after 2038 this code won't work. As + mysql_priv.h currently lists #define TIMESTAMP_MAX_YEAR 2038 (see TIME_to_timestamp()) */ - my_tz_UTC->gmt_sec_to_TIME(<ime,t=TIME_to_timestamp(thd, <ime, ¬_used)); + my_tz_UTC->gmt_sec_to_TIME(<ime,t=TIME_to_timestamp(thd, <ime, + ¬_used)); if (!t) goto wrong_value; @@ -464,13 +514,13 @@ Event_parse_data::init_ends(THD *thd) goto error_bad_params; /* - This may result in a 1970-01-01 date if ltime is > 2037-xx-xx. - CONVERT_TZ has similar problem. - mysql_priv.h currently lists + Again, after 2038 this code won't work. As + mysql_priv.h currently lists #define TIMESTAMP_MAX_YEAR 2038 (see TIME_to_timestamp()) */ DBUG_PRINT("info", ("get the UTC time")); - my_tz_UTC->gmt_sec_to_TIME(<ime,t=TIME_to_timestamp(thd, <ime, ¬_used)); + my_tz_UTC->gmt_sec_to_TIME(<ime,t=TIME_to_timestamp(thd, <ime, + ¬_used)); if (!t) goto error_bad_params; @@ -485,7 +535,7 @@ Event_parse_data::init_ends(THD *thd) Check whether ENDS is not in the past. */ DBUG_PRINT("info", ("ENDS after NOW?")); - my_tz_UTC->gmt_sec_to_TIME(<ime_now, thd->query_start()); + my_tz_UTC->gmt_sec_to_TIME(<ime_now, (my_time_t)thd->query_start()); if (my_time_compare(<ime_now, <ime) == 1) goto error_bad_params; @@ -536,8 +586,9 @@ Event_parse_data::check_parse_data(THD *thd) { bool ret; DBUG_ENTER("Event_parse_data::check_parse_data"); - DBUG_PRINT("info", ("execute_at=0x%lx expr=0x%lx starts=0x%lx ends=0x%lx", - item_execute_at, item_expression, item_starts, item_ends)); + DBUG_PRINT("info", ("execute_at: 0x%lx expr=0x%lx starts=0x%lx ends=0x%lx", + (long) item_execute_at, (long) item_expression, + (long) item_starts, (long) item_ends)); init_name(thd, identifier); @@ -560,16 +611,18 @@ Event_parse_data::check_parse_data(THD *thd) void Event_parse_data::init_definer(THD *thd) { - int definer_user_len; - int definer_host_len; DBUG_ENTER("Event_parse_data::init_definer"); - DBUG_PRINT("info",("init definer_user thd->mem_root=0x%lx " - "thd->sec_ctx->priv_user=0x%lx", thd->mem_root, - thd->security_ctx->priv_user)); + DBUG_ASSERT(thd->lex->definer); + + const char *definer_user= thd->lex->definer->user.str; + const char *definer_host= thd->lex->definer->host.str; + int definer_user_len= thd->lex->definer->user.length; + int definer_host_len= thd->lex->definer->host.length; - definer_user_len= strlen(thd->security_ctx->priv_user); - definer_host_len= strlen(thd->security_ctx->priv_host); + DBUG_PRINT("info",("init definer_user thd->mem_root: 0x%lx " + "definer_user: 0x%lx", (long) thd->mem_root, + (long) definer_user)); /* + 1 for @ */ DBUG_PRINT("info",("init definer as whole")); @@ -577,12 +630,11 @@ Event_parse_data::init_definer(THD *thd) definer.str= thd->alloc(definer.length + 1); DBUG_PRINT("info",("copy the user")); - memcpy(definer.str, thd->security_ctx->priv_user, definer_user_len); + memcpy(definer.str, definer_user, definer_user_len); definer.str[definer_user_len]= '@'; DBUG_PRINT("info",("copy the host")); - memcpy(definer.str + definer_user_len + 1, thd->security_ctx->priv_host, - definer_host_len); + memcpy(definer.str + definer_user_len + 1, definer_host, definer_host_len); definer.str[definer.length]= '\0'; DBUG_PRINT("info",("definer [%s] initted", definer.str)); @@ -735,7 +787,7 @@ Event_timed::~Event_timed() */ Event_job_data::Event_job_data() - :thd(NULL), sphead(NULL), sql_mode(0) + :sphead(NULL), sql_mode(0) { } @@ -886,14 +938,29 @@ Event_queue_element::load_from_row(TABLE *table) goto error; /* - In DB the values start from 1 but enum interval_type starts - from 0 + We load the interval type from disk as string and then map it to + an integer. This decouples the values of enum interval_type + and values actually stored on disk. Therefore the type can be + reordered without risking incompatibilities of data between versions. */ if (!table->field[ET_FIELD_TRANSIENT_INTERVAL]->is_null()) - interval= (interval_type) ((ulonglong) - table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_int() - 1); - else - interval= (interval_type) 0; + { + int i; + char buff[MAX_FIELD_WIDTH]; + String str(buff, sizeof(buff), &my_charset_bin); + LEX_STRING tmp; + + table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_str(&str); + if (!(tmp.length= str.length())) + goto error; + + tmp.str= str.c_ptr_safe(); + + i= find_string_in_array(interval_type_to_name, &tmp, system_charset_info); + if (i < 0) + goto error; + interval= (interval_type) i; + } table->field[ET_FIELD_LAST_EXECUTED]->get_date(&last_executed, TIME_NO_ZERO_DATE); @@ -1017,8 +1084,9 @@ bool get_next_time(TIME *next, TIME *start, TIME *time_now, TIME *last_exec, TIME tmp; longlong months=0, seconds=0; DBUG_ENTER("get_next_time"); - DBUG_PRINT("enter", ("start=%llu now=%llu", TIME_to_ulonglong_datetime(start), - TIME_to_ulonglong_datetime(time_now))); + DBUG_PRINT("enter", ("start: %lu now: %lu", + (long) TIME_to_ulonglong_datetime(start), + (long) TIME_to_ulonglong_datetime(time_now))); bzero(&interval, sizeof(interval)); @@ -1066,7 +1134,7 @@ bool get_next_time(TIME *next, TIME *start, TIME *time_now, TIME *last_exec, case INTERVAL_LAST: DBUG_ASSERT(0); } - DBUG_PRINT("info", ("seconds=%ld months=%ld", seconds, months)); + DBUG_PRINT("info", ("seconds: %ld months: %ld", (long) seconds, (long) months)); if (seconds) { longlong seconds_diff; @@ -1077,21 +1145,21 @@ bool get_next_time(TIME *next, TIME *start, TIME *time_now, TIME *last_exec, DBUG_PRINT("error", ("negative difference")); DBUG_ASSERT(0); } - uint multiplier= seconds_diff / seconds; + uint multiplier= (uint) (seconds_diff / seconds); /* Increase the multiplier is the modulus is not zero to make round up. Or if time_now==start then we should not execute the same event two times for the same time get the next exec if the modulus is not */ - DBUG_PRINT("info", ("multiplier=%d", multiplier)); + DBUG_PRINT("info", ("multiplier: %d", multiplier)); if (seconds_diff % seconds || (!seconds_diff && last_exec->year) || TIME_to_ulonglong_datetime(time_now) == TIME_to_ulonglong_datetime(last_exec)) ++multiplier; interval.second= seconds * multiplier; - DBUG_PRINT("info", ("multiplier=%u interval.second=%u", multiplier, - interval.second)); + DBUG_PRINT("info", ("multiplier: %lu interval.second: %lu", (ulong) multiplier, + (ulong) interval.second)); tmp= *start; if (!(ret= date_add_interval(&tmp, INTERVAL_SECOND, interval))) *next= tmp; @@ -1110,7 +1178,7 @@ bool get_next_time(TIME *next, TIME *start, TIME *time_now, TIME *last_exec, directly with +1 we will be after the current date but it could be that we will be 1 month ahead, so 2 steps are necessary. */ - interval.month= (diff_months / months)*months; + interval.month= (ulong) ((diff_months / months)*months); /* Check if the same month as last_exec (always set - prerequisite) An event happens at most once per month so there is no way to @@ -1123,7 +1191,7 @@ bool get_next_time(TIME *next, TIME *start, TIME *time_now, TIME *last_exec, */ if (time_now->year == last_exec->year && time_now->month == last_exec->month) - interval.month+= months; + interval.month+= (ulong) months; tmp= *start; if ((ret= date_add_interval(&tmp, INTERVAL_MONTH, interval))) @@ -1132,7 +1200,7 @@ bool get_next_time(TIME *next, TIME *start, TIME *time_now, TIME *last_exec, /* If `tmp` is still before time_now just add one more time the interval */ if (my_time_compare(&tmp, time_now) == -1) { - interval.month+= months; + interval.month+= (ulong) months; tmp= *start; if ((ret= date_add_interval(&tmp, INTERVAL_MONTH, interval))) goto done; @@ -1143,7 +1211,7 @@ bool get_next_time(TIME *next, TIME *start, TIME *time_now, TIME *last_exec, } done: - DBUG_PRINT("info", ("next=%llu", TIME_to_ulonglong_datetime(next))); + DBUG_PRINT("info", ("next: %lu", (long) TIME_to_ulonglong_datetime(next))); DBUG_RETURN(ret); } @@ -1168,17 +1236,17 @@ Event_queue_element::compute_next_execution_time() { TIME time_now; int tmp; - DBUG_ENTER("Event_queue_element::compute_next_execution_time"); - DBUG_PRINT("enter", ("starts=%llu ends=%llu last_executed=%llu this=0x%lx", - TIME_to_ulonglong_datetime(&starts), - TIME_to_ulonglong_datetime(&ends), - TIME_to_ulonglong_datetime(&last_executed), this)); + DBUG_PRINT("enter", ("starts: %lu ends: %lu last_executed: %lu this: 0x%lx", + (long) TIME_to_ulonglong_datetime(&starts), + (long) TIME_to_ulonglong_datetime(&ends), + (long) TIME_to_ulonglong_datetime(&last_executed), + (long) this)); if (status == Event_queue_element::DISABLED) { DBUG_PRINT("compute_next_execution_time", - ("Event %s is DISABLED", name.str)); + ("Event %s is DISABLED", name.str)); goto ret; } /* If one-time, no need to do computation */ @@ -1188,9 +1256,9 @@ Event_queue_element::compute_next_execution_time() if (last_executed.year) { DBUG_PRINT("info",("One-time event %s.%s of was already executed", - dbname.str, name.str, definer.str)); + dbname.str, name.str)); dropped= (on_completion == Event_queue_element::ON_COMPLETION_DROP); - DBUG_PRINT("info",("One-time event will be dropped=%d.", dropped)); + DBUG_PRINT("info",("One-time event will be dropped: %d.", dropped)); status= Event_queue_element::DISABLED; status_changed= TRUE; @@ -1198,9 +1266,10 @@ Event_queue_element::compute_next_execution_time() goto ret; } - my_tz_UTC->gmt_sec_to_TIME(&time_now, current_thd->query_start()); + my_tz_UTC->gmt_sec_to_TIME(&time_now, (my_time_t)current_thd->query_start()); - DBUG_PRINT("info",("NOW=[%llu]", TIME_to_ulonglong_datetime(&time_now))); + DBUG_PRINT("info",("NOW: [%lu]", + (ulong) TIME_to_ulonglong_datetime(&time_now))); /* if time_now is after ends don't execute anymore */ if (!ends_null && (tmp= my_time_compare(&ends, &time_now)) == -1) @@ -1211,9 +1280,10 @@ Event_queue_element::compute_next_execution_time() execute_at_null= TRUE; if (on_completion == Event_queue_element::ON_COMPLETION_DROP) dropped= TRUE; - DBUG_PRINT("info", ("Dropped=%d", dropped)); + DBUG_PRINT("info", ("Dropped: %d", dropped)); status= Event_queue_element::DISABLED; status_changed= TRUE; + dropped= TRUE; goto ret; } @@ -1264,7 +1334,7 @@ Event_queue_element::compute_next_execution_time() if (get_next_time(&next_exec, &starts, &time_now, last_executed.year? &last_executed:&starts, - expression, interval)) + (int) expression, interval)) goto err; /* There was previous execution */ @@ -1282,7 +1352,8 @@ Event_queue_element::compute_next_execution_time() } else { - DBUG_PRINT("info",("Next[%llu]",TIME_to_ulonglong_datetime(&next_exec))); + DBUG_PRINT("info",("Next[%lu]", + (ulong) TIME_to_ulonglong_datetime(&next_exec))); execute_at= next_exec; execute_at_null= FALSE; } @@ -1301,10 +1372,11 @@ Event_queue_element::compute_next_execution_time() { TIME next_exec; if (get_next_time(&next_exec, &starts, &time_now, &last_executed, - expression, interval)) + (int) expression, interval)) goto err; execute_at= next_exec; - DBUG_PRINT("info",("Next[%llu]",TIME_to_ulonglong_datetime(&next_exec))); + DBUG_PRINT("info",("Next[%lu]", + (ulong) TIME_to_ulonglong_datetime(&next_exec))); } else { @@ -1335,10 +1407,11 @@ Event_queue_element::compute_next_execution_time() TIME next_exec; if (get_next_time(&next_exec, &starts, &time_now, last_executed.year? &last_executed:&starts, - expression, interval)) + (int) expression, interval)) goto err; execute_at= next_exec; - DBUG_PRINT("info",("Next[%llu]",TIME_to_ulonglong_datetime(&next_exec))); + DBUG_PRINT("info",("Next[%lu]", + (ulong) TIME_to_ulonglong_datetime(&next_exec))); } execute_at_null= FALSE; } @@ -1360,7 +1433,7 @@ Event_queue_element::compute_next_execution_time() TIME next_exec; if (get_next_time(&next_exec, &starts, &time_now, &last_executed, - expression, interval)) + (int) expression, interval)) goto err; if (my_time_compare(&ends, &next_exec) == -1) @@ -1375,8 +1448,8 @@ Event_queue_element::compute_next_execution_time() } else { - DBUG_PRINT("info", ("Next[%llu]", - TIME_to_ulonglong_datetime(&next_exec))); + DBUG_PRINT("info", ("Next[%lu]", + (ulong) TIME_to_ulonglong_datetime(&next_exec))); execute_at= next_exec; execute_at_null= FALSE; } @@ -1385,8 +1458,8 @@ Event_queue_element::compute_next_execution_time() goto ret; } ret: - DBUG_PRINT("info", ("ret=0 execute_at=%llu", - TIME_to_ulonglong_datetime(&execute_at))); + DBUG_PRINT("info", ("ret: 0 execute_at: %lu", + (long) TIME_to_ulonglong_datetime(&execute_at))); DBUG_RETURN(FALSE); err: DBUG_PRINT("info", ("ret=1")); @@ -1419,32 +1492,6 @@ Event_queue_element::mark_last_executed(THD *thd) /* - Drops the event - - SYNOPSIS - Event_queue_element::drop() - thd thread context - - RETURN VALUE - 0 OK - -1 Cannot open mysql.event - -2 Cannot find the event in mysql.event (already deleted?) - - others return code from SE in case deletion of the event row - failed. -*/ - -int -Event_queue_element::drop(THD *thd) -{ - DBUG_ENTER("Event_queue_element::drop"); - - DBUG_RETURN(Events::get_instance()-> - drop_event(thd, dbname, name, FALSE, TRUE)); -} - - -/* Saves status and last_executed_at to the disk if changed. SYNOPSIS @@ -1475,13 +1522,13 @@ Event_queue_element::update_timing_fields(THD *thd) thd->reset_n_backup_open_tables_state(&backup); - if (Events::get_instance()->open_event_table(thd, TL_WRITE, &table)) + if (events_event_db_repository.open_event_table(thd, TL_WRITE, &table)) { ret= TRUE; goto done; } fields= table->field; - if ((ret= Events::get_instance()->db_repository-> + if ((ret= events_event_db_repository. find_named_event(thd, dbname, name, table))) goto done; @@ -1532,7 +1579,6 @@ done: int Event_timed::get_create_event(THD *thd, String *buf) { - int multipl= 0; char tmp_buf[2 * STRING_BUFFER_USUAL_SIZE]; String expr_buf(tmp_buf, sizeof(tmp_buf), system_charset_info); expr_buf.length(0); @@ -1673,7 +1719,7 @@ done: thd->end_statement(); thd->cleanup_after_query(); - DBUG_PRINT("info", ("EXECUTED %s.%s ret=%d", dbname.str, name.str, ret)); + DBUG_PRINT("info", ("EXECUTED %s.%s ret: %d", dbname.str, name.str, ret)); DBUG_RETURN(ret); } @@ -1737,7 +1783,7 @@ Event_job_data::compile(THD *thd, MEM_ROOT *mem_root) thd->update_charset(); - DBUG_PRINT("info",("old_sql_mode=%d new_sql_mode=%d",old_sql_mode, sql_mode)); + DBUG_PRINT("info",("old_sql_mode: %lu new_sql_mode: %lu",old_sql_mode, sql_mode)); thd->variables.sql_mode= this->sql_mode; /* Change the memory root for the execution time */ if (mem_root) @@ -1754,7 +1800,7 @@ Event_job_data::compile(THD *thd, MEM_ROOT *mem_root) thd->query= show_create.c_ptr_safe(); thd->query_length= show_create.length(); - DBUG_PRINT("info", ("query:%s",thd->query)); + DBUG_PRINT("info", ("query: %s",thd->query)); event_change_security_context(thd, definer_user, definer_host, dbname, &save_ctx); @@ -1762,19 +1808,14 @@ Event_job_data::compile(THD *thd, MEM_ROOT *mem_root) mysql_init_query(thd, (uchar*) thd->query, thd->query_length); if (MYSQLparse((void *)thd) || thd->is_fatal_error) { - DBUG_PRINT("error", ("error during compile or thd->is_fatal_error=%d", + DBUG_PRINT("error", ("error during compile or thd->is_fatal_error: %d", thd->is_fatal_error)); - /* - Free lex associated resources - QQ: Do we really need all this stuff here? - */ + lex.unit.cleanup(); + sql_print_error("SCHEDULER: Error during compilation of %s.%s or " - "thd->is_fatal_error=%d", + "thd->is_fatal_error: %d", dbname.str, name.str, thd->is_fatal_error); - lex.unit.cleanup(); - delete lex.sphead; - sphead= lex.sphead= NULL; ret= EVEX_COMPILE_ERROR; goto done; } diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h index e7e96d299fb..4346b0eb5b8 100644 --- a/sql/event_data_objects.h +++ b/sql/event_data_objects.h @@ -4,8 +4,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -28,6 +27,27 @@ class sp_head; class Sql_alloc; +class Event_queue_element_for_exec +{ +public: + Event_queue_element_for_exec(){}; + ~Event_queue_element_for_exec(); + + bool + init(LEX_STRING dbname, LEX_STRING name); + + LEX_STRING dbname; + LEX_STRING name; + bool dropped; + THD *thd; + +private: + /* Prevent use of these */ + Event_queue_element_for_exec(const Event_queue_element_for_exec &); + void operator=(Event_queue_element_for_exec &); +}; + + class Event_basic { protected: @@ -97,9 +117,6 @@ public: bool compute_next_execution_time(); - int - drop(THD *thd); - void mark_last_executed(THD *thd); @@ -111,14 +128,14 @@ public: void *p; DBUG_ENTER("Event_queue_element::new(size)"); p= my_malloc(size, MYF(0)); - DBUG_PRINT("info", ("alloc_ptr=0x%lx", p)); + DBUG_PRINT("info", ("alloc_ptr: 0x%lx", (long) p)); DBUG_RETURN(p); } static void operator delete(void *ptr, size_t size) { DBUG_ENTER("Event_queue_element::delete(ptr,size)"); - DBUG_PRINT("enter", ("free_ptr=0x%lx", ptr)); + DBUG_PRINT("enter", ("free_ptr: 0x%lx", (long) ptr)); TRASH(ptr, size); my_free((gptr) ptr, MYF(0)); DBUG_VOID_RETURN; @@ -161,7 +178,6 @@ public: class Event_job_data : public Event_basic { public: - THD *thd; sp_head *sphead; LEX_STRING body; diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 464c26044c7..940930ec4c6 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -188,11 +187,11 @@ mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et, fields[ET_FIELD_INTERVAL_EXPR]->store((longlong)et->expression, TRUE); fields[ET_FIELD_TRANSIENT_INTERVAL]->set_notnull(); - /* - In the enum (C) intervals start from 0 but in mysql enum valid values - start from 1. Thus +1 offset is needed! - */ - fields[ET_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->interval+1, TRUE); + + fields[ET_FIELD_TRANSIENT_INTERVAL]-> + store(interval_type_to_name[et->interval].str, + interval_type_to_name[et->interval].length, + scs); fields[ET_FIELD_EXECUTE_AT]->set_null(); @@ -519,7 +518,6 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, my_bool create_if_not) { int ret= 0; - CHARSET_INFO *scs= system_charset_info; TABLE *table= NULL; char old_db_buf[NAME_LEN+1]; LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) }; @@ -958,7 +956,7 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname, Open_tables_state backup; DBUG_ENTER("Event_db_repository::load_named_event"); - DBUG_PRINT("enter",("thd=0x%lx name:%*s",thd, name.length, name.str)); + DBUG_PRINT("enter",("thd: 0x%lx name: %*s", (long) thd, name.length, name.str)); thd->reset_n_backup_open_tables_state(&backup); diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h index ed74edd7e19..1457fb64e2e 100644 --- a/sql/event_db_repository.h +++ b/sql/event_db_repository.h @@ -4,8 +4,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/event_queue.cc b/sql/event_queue.cc index 527a59018a8..fb1653d6d8f 100644 --- a/sql/event_queue.cc +++ b/sql/event_queue.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -17,7 +16,6 @@ #include "mysql_priv.h" #include "event_queue.h" #include "event_data_objects.h" -#include "event_db_repository.h" #define EVENT_QUEUE_INITIAL_SIZE 30 @@ -137,16 +135,12 @@ Event_queue::deinit_mutexes() */ bool -Event_queue::init_queue(THD *thd, Event_db_repository *db_repo) +Event_queue::init_queue(THD *thd) { - bool res; - struct event_queue_param *event_queue_param_value= NULL; - DBUG_ENTER("Event_queue::init_queue"); - DBUG_PRINT("enter", ("this=0x%lx", this)); + DBUG_PRINT("enter", ("this: 0x%lx", (long) this)); LOCK_QUEUE_DATA(); - db_repository= db_repo; if (init_queue_ex(&queue, EVENT_QUEUE_INITIAL_SIZE , 0 /*offset*/, 0 /*max_on_top*/, event_queue_element_compare_q, @@ -156,20 +150,8 @@ Event_queue::init_queue(THD *thd, Event_db_repository *db_repo) goto err; } - if (sizeof(my_time_t) != sizeof(time_t)) - { - sql_print_error("SCHEDULER: sizeof(my_time_t) != sizeof(time_t) ." - "The scheduler may not work correctly. Stopping"); - DBUG_ASSERT(0); - goto err; - } - - res= load_events_from_db(thd); UNLOCK_QUEUE_DATA(); - if (res) - deinit_queue(); - - DBUG_RETURN(res); + DBUG_RETURN(FALSE); err: UNLOCK_QUEUE_DATA(); @@ -206,37 +188,29 @@ Event_queue::deinit_queue() Event_queue::create_event() dbname The schema of the new event name The name of the new event - - RETURN VALUE - OP_OK OK or scheduler not working - OP_LOAD_ERROR Error during loading from disk */ -int -Event_queue::create_event(THD *thd, LEX_STRING dbname, LEX_STRING name) +void +Event_queue::create_event(THD *thd, Event_queue_element *new_element) { - int res; - Event_queue_element *new_element; DBUG_ENTER("Event_queue::create_event"); - DBUG_PRINT("enter", ("thd=0x%lx et=%s.%s",thd, dbname.str, name.str)); + DBUG_PRINT("enter", ("thd: 0x%lx et=%s.%s", (long) thd, + new_element->dbname.str, new_element->name.str)); - new_element= new Event_queue_element(); - res= db_repository->load_named_event(thd, dbname, name, new_element); - if (res || new_element->status == Event_queue_element::DISABLED) + if (new_element->status == Event_queue_element::DISABLED) delete new_element; else { new_element->compute_next_execution_time(); + DBUG_PRINT("info", ("new event in the queue: 0x%lx", (long) new_element)); LOCK_QUEUE_DATA(); - DBUG_PRINT("info", ("new event in the queue 0x%lx", new_element)); queue_insert_safe(&queue, (byte *) new_element); dbug_dump_queue(thd->query_start()); pthread_cond_broadcast(&COND_queue_state); UNLOCK_QUEUE_DATA(); } - - DBUG_RETURN(res); + DBUG_VOID_RETURN; } @@ -250,32 +224,16 @@ Event_queue::create_event(THD *thd, LEX_STRING dbname, LEX_STRING name) name Name of the event new_schema New schema, in case of RENAME TO, otherwise NULL new_name New name, in case of RENAME TO, otherwise NULL - - RETURN VALUE - OP_OK OK or scheduler not working - OP_LOAD_ERROR Error during loading from disk */ -int +void Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name, - LEX_STRING *new_schema, LEX_STRING *new_name) + Event_queue_element *new_element) { - int res; - Event_queue_element *new_element; - DBUG_ENTER("Event_queue::update_event"); - DBUG_PRINT("enter", ("thd=0x%lx et=[%s.%s]", thd, dbname.str, name.str)); - - new_element= new Event_queue_element(); + DBUG_PRINT("enter", ("thd: 0x%lx et=[%s.%s]", (long) thd, dbname.str, name.str)); - res= db_repository->load_named_event(thd, new_schema ? *new_schema:dbname, - new_name ? *new_name:name, new_element); - if (res) - { - delete new_element; - goto end; - } - else if (new_element->status == Event_queue_element::DISABLED) + if (new_element->status == Event_queue_element::DISABLED) { DBUG_PRINT("info", ("The event is disabled.")); /* @@ -294,7 +252,7 @@ Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name, /* If not disabled event */ if (new_element) { - DBUG_PRINT("info", ("new event in the Q 0x%lx", new_element)); + DBUG_PRINT("info", ("new event in the queue: 0x%lx", (long) new_element)); queue_insert_safe(&queue, (byte *) new_element); pthread_cond_broadcast(&COND_queue_state); } @@ -302,9 +260,7 @@ Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name, dbug_dump_queue(thd->query_start()); UNLOCK_QUEUE_DATA(); -end: - DBUG_PRINT("info", ("res=%d", res)); - DBUG_RETURN(res); + DBUG_VOID_RETURN; } @@ -322,7 +278,8 @@ void Event_queue::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name) { DBUG_ENTER("Event_queue::drop_event"); - DBUG_PRINT("enter", ("thd=0x%lx db=%s name=%s", thd, dbname.str, name.str)); + DBUG_PRINT("enter", ("thd: 0x%lx db :%s name: %s", (long) thd, + dbname.str, name.str)); LOCK_QUEUE_DATA(); find_n_remove_event(dbname, name); @@ -455,134 +412,6 @@ Event_queue::find_n_remove_event(LEX_STRING db, LEX_STRING name) /* - Loads all ENABLED events from mysql.event into the prioritized - queue. Called during scheduler main thread initialization. Compiles - the events. Creates Event_queue_element instances for every ENABLED event - from mysql.event. - - SYNOPSIS - Event_queue::load_events_from_db() - thd - Thread context. Used for memory allocation in some cases. - - RETURN VALUE - 0 OK - !0 Error (EVEX_OPEN_TABLE_FAILED, EVEX_MICROSECOND_UNSUP, - EVEX_COMPILE_ERROR) - in all these cases mysql.event was - tampered. - - NOTES - Reports the error to the console -*/ - -int -Event_queue::load_events_from_db(THD *thd) -{ - TABLE *table; - READ_RECORD read_record_info; - int ret= -1; - uint count= 0; - bool clean_the_queue= TRUE; - - DBUG_ENTER("Event_queue::load_events_from_db"); - DBUG_PRINT("enter", ("thd=0x%lx", thd)); - - if ((ret= db_repository->open_event_table(thd, TL_READ, &table))) - { - sql_print_error("SCHEDULER: Table mysql.event is damaged. Can not open"); - DBUG_RETURN(EVEX_OPEN_TABLE_FAILED); - } - - init_read_record(&read_record_info, thd, table ,NULL,1,0); - while (!(read_record_info.read_record(&read_record_info))) - { - Event_queue_element *et; - if (!(et= new Event_queue_element)) - { - DBUG_PRINT("info", ("Out of memory")); - break; - } - DBUG_PRINT("info", ("Loading event from row.")); - - if ((ret= et->load_from_row(table))) - { - sql_print_error("SCHEDULER: Error while loading from mysql.event. " - "Table probably corrupted"); - break; - } - if (et->status != Event_queue_element::ENABLED) - { - DBUG_PRINT("info",("%s is disabled",et->name.str)); - delete et; - continue; - } - - /* let's find when to be executed */ - if (et->compute_next_execution_time()) - { - sql_print_error("SCHEDULER: Error while computing execution time of %s.%s." - " Skipping", et->dbname.str, et->name.str); - continue; - } - - { - Event_job_data temp_job_data; - DBUG_PRINT("info", ("Event %s loaded from row. ", et->name.str)); - - temp_job_data.load_from_row(table); - - /* - We load only on scheduler root just to check whether the body - compiles. - */ - switch (ret= temp_job_data.compile(thd, thd->mem_root)) { - case EVEX_MICROSECOND_UNSUP: - sql_print_error("SCHEDULER: mysql.event is tampered. MICROSECOND is not " - "supported but found in mysql.event"); - break; - case EVEX_COMPILE_ERROR: - sql_print_error("SCHEDULER: Error while compiling %s.%s. Aborting load", - et->dbname.str, et->name.str); - break; - default: - break; - } - thd->end_statement(); - thd->cleanup_after_query(); - } - if (ret) - { - delete et; - goto end; - } - - DBUG_PRINT("load_events_from_db", ("Adding 0x%lx to the exec list.")); - queue_insert_safe(&queue, (byte *) et); - count++; - } - clean_the_queue= FALSE; -end: - end_read_record(&read_record_info); - - if (clean_the_queue) - { - empty_queue(); - ret= -1; - } - else - { - ret= 0; - sql_print_information("SCHEDULER: Loaded %d event%s", count, - (count == 1)?"":"s"); - } - - close_thread_tables(thd); - - DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count)); - DBUG_RETURN(ret); -} - - -/* Recalculates activation times in the queue. There is one reason for that. Because the values (execute_at) by which the queue is ordered are changed by calls to compute_next_execution_time() on a request from the @@ -631,7 +460,7 @@ Event_queue::empty_queue() { uint i; DBUG_ENTER("Event_queue::empty_queue"); - DBUG_PRINT("enter", ("Purging the queue. %d element(s)", queue.elements)); + DBUG_PRINT("enter", ("Purging the queue. %u element(s)", queue.elements)); sql_print_information("SCHEDULER: Purging queue. %u events", queue.elements); /* empty the queue */ for (i= 0; i < queue.elements; ++i) @@ -663,16 +492,20 @@ Event_queue::dbug_dump_queue(time_t now) for (i = 0; i < queue.elements; i++) { et= ((Event_queue_element*)queue_element(&queue, i)); - DBUG_PRINT("info",("et=0x%lx db=%s name=%s",et, et->dbname.str, et->name.str)); - DBUG_PRINT("info", ("exec_at=%llu starts=%llu ends=%llu execs_so_far=%u" - " expr=%lld et.exec_at=%d now=%d (et.exec_at - now)=%d if=%d", - TIME_to_ulonglong_datetime(&et->execute_at), - TIME_to_ulonglong_datetime(&et->starts), - TIME_to_ulonglong_datetime(&et->ends), - et->execution_count, - et->expression, sec_since_epoch_TIME(&et->execute_at), now, - (int)(sec_since_epoch_TIME(&et->execute_at) - now), - sec_since_epoch_TIME(&et->execute_at) <= now)); + DBUG_PRINT("info", ("et: 0x%lx name: %s.%s", (long) et, + et->dbname.str, et->name.str)); + DBUG_PRINT("info", ("exec_at: %lu starts: %lu ends: %lu execs_so_far: %u " + "expr: %ld et.exec_at: %ld now: %ld " + "(et.exec_at - now): %d if: %d", + (long) TIME_to_ulonglong_datetime(&et->execute_at), + (long) TIME_to_ulonglong_datetime(&et->starts), + (long) TIME_to_ulonglong_datetime(&et->ends), + et->execution_count, + (long) et->expression, + (long) (sec_since_epoch_TIME(&et->execute_at)), + (long) now, + (int) (sec_since_epoch_TIME(&et->execute_at) - now), + sec_since_epoch_TIME(&et->execute_at) <= now)); } DBUG_VOID_RETURN; #endif @@ -688,136 +521,106 @@ static const char *queue_wait_msg= "Waiting for next activation"; SYNOPSIS Event_queue::get_top_for_execution_if_time() - thd [in] Thread - now [in] Current timestamp - job_data [out] The object to execute - abstime [out] Time to sleep + thd [in] Thread + event_name [out] The object to execute RETURN VALUE - FALSE No error. If *job_data==NULL then top not elligible for execution. - Could be that there is no top. If abstime->tv_sec is set to value - greater than zero then use abstime with pthread_cond_timedwait(). - If abstime->tv_sec is zero then sleep with pthread_cond_wait(). - abstime->tv_nsec is always zero. - TRUE Error - + FALSE No error. event_name != NULL + TRUE Serious error */ bool -Event_queue::get_top_for_execution_if_time(THD *thd, Event_job_data **job_data) +Event_queue::get_top_for_execution_if_time(THD *thd, + Event_queue_element_for_exec **event_name) { bool ret= FALSE; struct timespec top_time; - struct timespec *abstime; - Event_queue_element *top= NULL; - bool to_free= FALSE; - bool to_drop= FALSE; - *job_data= NULL; + *event_name= NULL; DBUG_ENTER("Event_queue::get_top_for_execution_if_time"); - top_time.tv_nsec= 0; LOCK_QUEUE_DATA(); for (;;) { - int res; + Event_queue_element *top= NULL; - thd->end_time(); - time_t now= thd->query_start(); - abstime= NULL; + /* Break loop if thd has been killed */ + if (thd->killed) + { + DBUG_PRINT("info", ("thd->killed=%d", thd->killed)); + goto end; + } - if (queue.elements) + if (!queue.elements) { - top= ((Event_queue_element*) queue_element(&queue, 0)); - top_time.tv_sec= sec_since_epoch_TIME(&top->execute_at); + /* There are no events in the queue */ + set_zero_time(&next_activation_at, MYSQL_TIMESTAMP_DATETIME); + + /* Wait on condition until signaled. Release LOCK_queue while waiting. */ + cond_wait(thd, NULL, queue_empty_msg, SCHED_FUNC, __LINE__); - abstime= &top_time; + continue; } - if (!abstime || abstime->tv_sec > now) + top= ((Event_queue_element*) queue_element(&queue, 0)); + + thd->end_time(); /* Get current time */ + + time_t seconds_to_next_event= + sec_since_epoch_TIME(&top->execute_at) - thd->query_start(); + next_activation_at= top->execute_at; + if (seconds_to_next_event > 0) { - const char *msg; - if (abstime) - { - next_activation_at= top->execute_at; - msg= queue_wait_msg; - } - else - { - set_zero_time(&next_activation_at, MYSQL_TIMESTAMP_DATETIME); - msg= queue_wait_msg; - } - - cond_wait(thd, abstime, msg, SCHED_FUNC, __LINE__); - if (thd->killed) - { - DBUG_PRINT("info", ("thd->killed=%d", thd->killed)); - goto end; - } /* - The queue could have been emptied. Therefore it's safe to start from - the beginning. Moreover, this way we will get also the new top, if - the element at the top has been changed. + Not yet time for top event, wait on condition with + time or until signaled. Release LOCK_queue while waiting. */ + set_timespec(top_time, seconds_to_next_event); + cond_wait(thd, &top_time, queue_wait_msg, SCHED_FUNC, __LINE__); + continue; } - DBUG_PRINT("info", ("Ready for execution")); - if (!(*job_data= new Event_job_data())) - { - ret= TRUE; - break; - } - if ((res= db_repository->load_named_event(thd, top->dbname, top->name, - *job_data))) + if (!(*event_name= new Event_queue_element_for_exec()) || + (*event_name)->init(top->dbname, top->name)) { - DBUG_PRINT("error", ("Got %d from load_named_event", res)); - delete *job_data; - *job_data= NULL; ret= TRUE; break; } + DBUG_PRINT("info", ("Ready for execution")); top->mark_last_executed(thd); if (top->compute_next_execution_time()) top->status= Event_queue_element::DISABLED; DBUG_PRINT("info", ("event %s status is %d", top->name.str, top->status)); - (*job_data)->execution_count= top->execution_count; + top->execution_count++; + (*event_name)->dropped= top->dropped; top->update_timing_fields(thd); - if (((top->execute_at.year && !top->expression) || top->execute_at_null) || - (top->status == Event_queue_element::DISABLED)) + if (top->status == Event_queue_element::DISABLED) { DBUG_PRINT("info", ("removing from the queue")); sql_print_information("SCHEDULER: Last execution of %s.%s. %s", top->dbname.str, top->name.str, top->dropped? "Dropping.":""); - to_free= TRUE; - to_drop= top->dropped; + delete top; queue_remove(&queue, 0); } else queue_replaced(&queue); - dbug_dump_queue(now); + dbug_dump_queue(thd->query_start()); break; } end: UNLOCK_QUEUE_DATA(); - if (to_drop) - { - DBUG_PRINT("info", ("Dropping from disk")); - top->drop(thd); - } - if (to_free) - delete top; - DBUG_PRINT("info", ("returning %d. et_new=0x%lx abstime.tv_sec=%d ", - ret, *job_data, abstime? abstime->tv_sec:0)); + DBUG_PRINT("info", ("returning %d et_new: 0x%lx ", + ret, (long) *event_name)); - if (*job_data) - DBUG_PRINT("info", ("db=%s name=%s definer=%s", (*job_data)->dbname.str, - (*job_data)->name.str, (*job_data)->definer.str)); + if (*event_name) + DBUG_PRINT("info", ("db: %s name: %s", + (*event_name)->dbname.str, (*event_name)->name.str)); DBUG_RETURN(ret); } diff --git a/sql/event_queue.h b/sql/event_queue.h index 5b70506d388..a1237e1b52c 100644 --- a/sql/event_queue.h +++ b/sql/event_queue.h @@ -4,8 +4,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -17,12 +16,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ class Event_basic; -class Event_db_repository; -class Event_job_data; class Event_queue_element; +class Event_queue_element_for_exec; class THD; -class Event_scheduler; class Event_queue { @@ -36,19 +33,19 @@ public: deinit_mutexes(); bool - init_queue(THD *thd, Event_db_repository *db_repo); + init_queue(THD *thd); void deinit_queue(); /* Methods for queue management follow */ - int - create_event(THD *thd, LEX_STRING dbname, LEX_STRING name); + void + create_event(THD *thd, Event_queue_element *new_element); - int + void update_event(THD *thd, LEX_STRING dbname, LEX_STRING name, - LEX_STRING *new_schema, LEX_STRING *new_name); + Event_queue_element *new_element); void drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name); @@ -60,14 +57,15 @@ public: recalculate_activation_times(THD *thd); bool - get_top_for_execution_if_time(THD *thd, Event_job_data **job_data); + get_top_for_execution_if_time(THD *thd, + Event_queue_element_for_exec **event_name); + void dump_internal_status(); - int - load_events_from_db(THD *thd); - + void + empty_queue(); protected: void find_n_remove_event(LEX_STRING db, LEX_STRING name); @@ -77,8 +75,6 @@ protected: drop_matching_events(THD *thd, LEX_STRING pattern, bool (*)(LEX_STRING, Event_basic *)); - void - empty_queue(); void dbug_dump_queue(time_t now); @@ -87,11 +83,7 @@ protected: pthread_mutex_t LOCK_event_queue; pthread_cond_t COND_queue_state; - Event_db_repository *db_repository; - - Event_scheduler *scheduler; - - /* The sorted queue with the Event_job_data objects */ + /* The sorted queue with the Event_queue_element objects */ QUEUE queue; TIME next_activation_at; diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index 6f9f6887c12..64bba756be9 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +18,7 @@ #include "event_data_objects.h" #include "event_scheduler.h" #include "event_queue.h" +#include "event_db_repository.h" #ifdef __GNUC__ #if __GNUC__ >= 2 @@ -35,6 +35,11 @@ extern pthread_attr_t connection_attrib; + +Event_db_repository *Event_worker_thread::db_repository; +Events *Event_worker_thread::events_facade; + + static const LEX_STRING scheduler_states_names[] = { @@ -61,8 +66,8 @@ struct scheduler_param { et The event itself */ -static void -evex_print_warnings(THD *thd, Event_job_data *et) +void +Event_worker_thread::print_warnings(THD *thd, Event_job_data *et) { MYSQL_ERROR *err; DBUG_ENTER("evex_print_warnings"); @@ -105,25 +110,22 @@ evex_print_warnings(THD *thd, Event_job_data *et) SYNOPSIS post_init_event_thread() thd Thread + + NOTES + Before this is called, one should not do any DBUG_XXX() calls. + */ bool post_init_event_thread(THD *thd) { - my_thread_init(); - pthread_detach_this_thread(); - thd->real_id= pthread_self(); + (void) init_new_connection_handler_thread(); if (init_thr_lock() || thd->store_globals()) { thd->cleanup(); return TRUE; } -#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) - sigset_t set; - VOID(sigemptyset(&set)); // Get mask in use - VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); -#endif pthread_mutex_lock(&LOCK_thread_count); threads.append(thd); thread_count++; @@ -188,7 +190,7 @@ pre_init_event_thread(THD* thd) thd->options|= OPTION_AUTO_IS_NULL; thd->client_capabilities|= CLIENT_MULTI_RESULTS; pthread_mutex_lock(&LOCK_thread_count); - thd->thread_id= thread_id++; + thd->thread_id= thd->variables.pseudo_thread_id= thread_id++; pthread_mutex_unlock(&LOCK_thread_count); /* @@ -219,20 +221,20 @@ pthread_handler_t event_scheduler_thread(void *arg) { /* needs to be first for thread_stack */ - THD *thd= (THD *)((struct scheduler_param *) arg)->thd; + THD *thd= (THD *) ((struct scheduler_param *) arg)->thd; Event_scheduler *scheduler= ((struct scheduler_param *) arg)->scheduler; - - my_free((char*)arg, MYF(0)); + bool res; thd->thread_stack= (char *)&thd; // remember where our stack is + res= post_init_event_thread(thd); DBUG_ENTER("event_scheduler_thread"); - - if (!post_init_event_thread(thd)) + my_free((char*)arg, MYF(0)); + if (!res) scheduler->run(thd); deinit_event_thread(thd); - + pthread_exit(0); DBUG_RETURN(0); // Against gcc warnings } @@ -252,50 +254,106 @@ event_scheduler_thread(void *arg) pthread_handler_t event_worker_thread(void *arg) { - /* needs to be first for thread_stack */ THD *thd; - Event_job_data *event= (Event_job_data *)arg; - int ret; + Event_queue_element_for_exec *event= (Event_queue_element_for_exec *)arg; thd= event->thd; - thd->thread_stack= (char *) &thd; // remember where our stack is - DBUG_ENTER("event_worker_thread"); + Event_worker_thread worker_thread; + worker_thread.run(thd, event); + + return 0; // Can't return anything here +} + + +/* + Function that executes an event in a child thread. Setups the + environment for the event execution and cleans after that. + + SYNOPSIS + Event_worker_thread::run() + thd Thread context + event The Event_queue_element_for_exec object to be processed +*/ + +void +Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event) +{ + /* needs to be first for thread_stack */ + char my_stack; + int ret; + Event_job_data *job_data= NULL; + bool res; + + thd->thread_stack= &my_stack; // remember where our stack is + res= post_init_event_thread(thd); + + DBUG_ENTER("Event_worker_thread::run"); + DBUG_PRINT("info", ("Time is %ld, THD: 0x%lx", + (long) time(NULL), (long) thd)); + + if (res) + goto end; - if (!post_init_event_thread(thd)) + if (!(job_data= new Event_job_data())) + goto end; + else if ((ret= db_repository-> + load_named_event(thd, event->dbname, event->name, job_data))) { - DBUG_PRINT("info", ("Baikonur, time is %d, BURAN reporting and operational." - "THD=0x%lx", time(NULL), thd)); - - sql_print_information("SCHEDULER: [%s.%s of %s] executing in thread %lu. " - "Execution %u", - event->dbname.str, event->name.str, - event->definer.str, thd->thread_id, - event->execution_count); - - thd->enable_slow_log= TRUE; - - ret= event->execute(thd); - - evex_print_warnings(thd, event); - - sql_print_information("SCHEDULER: [%s.%s of %s] executed in thread %lu. " - "RetCode=%d", event->dbname.str, event->name.str, - event->definer.str, thd->thread_id, ret); - if (ret == EVEX_COMPILE_ERROR) - sql_print_information("SCHEDULER: COMPILE ERROR for event %s.%s of %s", - event->dbname.str, event->name.str, - event->definer.str); - else if (ret == EVEX_MICROSECOND_UNSUP) - sql_print_information("SCHEDULER: MICROSECOND is not supported"); + DBUG_PRINT("error", ("Got %d from load_named_event", ret)); + goto end; } - DBUG_PRINT("info", ("BURAN %s.%s is landing!", event->dbname.str, + + sql_print_information("SCHEDULER: [%s.%s of %s] executing in thread %lu. ", + job_data->dbname.str, job_data->name.str, + job_data->definer.str, thd->thread_id); + + thd->enable_slow_log= TRUE; + + ret= job_data->execute(thd); + + print_warnings(thd, job_data); + + sql_print_information("SCHEDULER: [%s.%s of %s] executed in thread %lu. " + "RetCode=%d", job_data->dbname.str, job_data->name.str, + job_data->definer.str, thd->thread_id, ret); + if (ret == EVEX_COMPILE_ERROR) + sql_print_information("SCHEDULER: COMPILE ERROR for event %s.%s of %s", + job_data->dbname.str, job_data->name.str, + job_data->definer.str); + else if (ret == EVEX_MICROSECOND_UNSUP) + sql_print_information("SCHEDULER: MICROSECOND is not supported"); + +end: + delete job_data; + + if (event->dropped) + { + sql_print_information("SCHEDULER: Dropping %s.%s", event->dbname.str, + event->name.str); + /* + Using db_repository can lead to a race condition because we access + the table without holding LOCK_metadata. + Scenario: + 1. CREATE EVENT xyz AT ... (conn thread) + 2. execute xyz (worker) + 3. CREATE EVENT XYZ EVERY ... (conn thread) + 4. drop xyz (worker) + 5. XYZ was just created on disk but `drop xyz` of the worker dropped it. + A consequent load to create Event_queue_element will fail. + + If all operations are performed under LOCK_metadata there is no such + problem. However, this comes at the price of introduction bi-directional + association between class Events and class Event_worker_thread. + */ + events_facade->drop_event(thd, event->dbname, event->name, FALSE); + } + DBUG_PRINT("info", ("Done with Event %s.%s", event->dbname.str, event->name.str)); - delete event; + delete event; deinit_event_thread(thd); - - DBUG_RETURN(0); // Can't return anything here + pthread_exit(0); } @@ -378,7 +436,7 @@ Event_scheduler::start() DBUG_ENTER("Event_scheduler::start"); LOCK_DATA(); - DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state])); + DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state].str)); if (state > INITIALIZED) goto end; @@ -400,7 +458,7 @@ Event_scheduler::start() scheduler_thd= new_thd; DBUG_PRINT("info", ("Setting state go RUNNING")); state= RUNNING; - DBUG_PRINT("info", ("Forking new thread for scheduduler. THD=0x%lx", new_thd)); + DBUG_PRINT("info", ("Forking new thread for scheduduler. THD: 0x%lx", (long) new_thd)); if (pthread_create(&th, &connection_attrib, event_scheduler_thread, (void*)scheduler_param_value)) { @@ -441,8 +499,6 @@ bool Event_scheduler::run(THD *thd) { int res= FALSE; - struct timespec abstime; - Event_job_data *job_data; DBUG_ENTER("Event_scheduler::run"); sql_print_information("SCHEDULER: Manager thread started with id %lu", @@ -455,18 +511,21 @@ Event_scheduler::run(THD *thd) while (is_running()) { + Event_queue_element_for_exec *event_name; + /* Gets a minimized version */ - if (queue->get_top_for_execution_if_time(thd, &job_data)) + if (queue->get_top_for_execution_if_time(thd, &event_name)) { sql_print_information("SCHEDULER: Serious error during getting next " "event to execute. Stopping"); break; } - DBUG_PRINT("info", ("get_top returned job_data=0x%lx", job_data)); - if (job_data) + DBUG_PRINT("info", ("get_top_for_execution_if_time returned " + "event_name=0x%lx", (long) event_name)); + if (event_name) { - if ((res= execute_top(thd, job_data))) + if ((res= execute_top(thd, event_name))) break; } else @@ -500,7 +559,7 @@ Event_scheduler::run(THD *thd) */ bool -Event_scheduler::execute_top(THD *thd, Event_job_data *job_data) +Event_scheduler::execute_top(THD *thd, Event_queue_element_for_exec *event_name) { THD *new_thd; pthread_t th; @@ -511,22 +570,22 @@ Event_scheduler::execute_top(THD *thd, Event_job_data *job_data) pre_init_event_thread(new_thd); new_thd->system_thread= SYSTEM_THREAD_EVENT_WORKER; - job_data->thd= new_thd; - DBUG_PRINT("info", ("BURAN %s@%s ready for start t-3..2..1..0..ignition", - job_data->dbname.str, job_data->name.str)); + event_name->thd= new_thd; + DBUG_PRINT("info", ("Event %s@%s ready for start", + event_name->dbname.str, event_name->name.str)); /* Major failure */ if ((res= pthread_create(&th, &connection_attrib, event_worker_thread, - job_data))) + event_name))) goto error; ++started_events; - DBUG_PRINT("info", ("Launch succeeded. BURAN is in THD=0x%lx", new_thd)); + DBUG_PRINT("info", ("Event is in THD: 0x%lx", (long) new_thd)); DBUG_RETURN(FALSE); error: - DBUG_PRINT("error", ("Baikonur, we have a problem! res=%d", res)); + DBUG_PRINT("error", ("Event_scheduler::execute_top() res: %d", res)); if (new_thd) { new_thd->proc_info= "Clearing"; @@ -538,7 +597,7 @@ error: delete new_thd; pthread_mutex_unlock(&LOCK_thread_count); } - delete job_data; + delete event_name; DBUG_RETURN(TRUE); } @@ -581,10 +640,10 @@ Event_scheduler::stop() { THD *thd= current_thd; DBUG_ENTER("Event_scheduler::stop"); - DBUG_PRINT("enter", ("thd=0x%lx", current_thd)); + DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd)); LOCK_DATA(); - DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state])); + DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state].str)); if (state != RUNNING) goto end; @@ -605,7 +664,7 @@ Event_scheduler::stop() */ state= STOPPING; - DBUG_PRINT("info", ("Manager thread has id %d", scheduler_thd->thread_id)); + DBUG_PRINT("info", ("Manager thread has id %lu", scheduler_thd->thread_id)); /* Lock from delete */ pthread_mutex_lock(&scheduler_thd->LOCK_delete); /* This will wake up the thread if it waits on Queue's conditional */ @@ -775,7 +834,7 @@ Event_scheduler::dump_internal_status() mutex_last_unlocked_at_line); printf("WOC : %s\n", waiting_on_cond? "YES":"NO"); printf("Workers : %u\n", workers_count()); - printf("Executed : %llu\n", started_events); + printf("Executed : %lu\n", (ulong) started_events); printf("Data locked: %s\n", mutex_scheduler_data_locked ? "YES":"NO"); DBUG_VOID_RETURN; diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h index dffaf8c056c..2ab21464057 100644 --- a/sql/event_scheduler.h +++ b/sql/event_scheduler.h @@ -4,8 +4,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -16,8 +15,11 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + class Event_queue; class Event_job_data; +class Event_db_repository; +class Events; void pre_init_event_thread(THD* thd); @@ -28,6 +30,29 @@ post_init_event_thread(THD* thd); void deinit_event_thread(THD *thd); + +class Event_worker_thread +{ +public: + static void + init(Events *events, Event_db_repository *db_repo) + { + db_repository= db_repo; + events_facade= events; + } + + void + run(THD *thd, Event_queue_element_for_exec *event); + +private: + void + print_warnings(THD *thd, Event_job_data *et); + + static Event_db_repository *db_repository; + static Events *events_facade; +}; + + class Event_scheduler { public: @@ -72,10 +97,9 @@ private: uint workers_count(); - /* helper functions */ bool - execute_top(THD *thd, Event_job_data *job_data); + execute_top(THD *thd, Event_queue_element_for_exec *event_name); /* helper functions for working with mutexes & conditionals */ void diff --git a/sql/events.cc b/sql/events.cc index 10a8be948ef..f73dc97e7c2 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -98,7 +97,7 @@ Event_queue events_event_queue; static Event_scheduler events_event_scheduler; -static + Event_db_repository events_event_db_repository; Events Events::singleton; @@ -297,29 +296,6 @@ Events::Events() /* - Opens mysql.event table with specified lock - - SYNOPSIS - Events::open_event_table() - thd Thread context - lock_type How to lock the table - table We will store the open table here - - RETURN VALUE - 1 Cannot lock table - 2 The table is corrupted - different number of fields - 0 OK -*/ - -int -Events::open_event_table(THD *thd, enum thr_lock_type lock_type, - TABLE **table) -{ - return db_repository->open_event_table(thd, lock_type, table); -} - - -/* The function exported to the world for creating of events. SYNOPSIS @@ -352,16 +328,24 @@ Events::create_event(THD *thd, Event_parse_data *parse_data, bool if_not_exists) /* On error conditions my_error() is called so no need to handle here */ if (!(ret= db_repository->create_event(thd, parse_data, if_not_exists))) { - if ((ret= event_queue->create_event(thd, parse_data->dbname, - parse_data->name))) + Event_queue_element *new_element; + + if (!(new_element= new Event_queue_element())) + ret= TRUE; // OOM + else if ((ret= db_repository->load_named_event(thd, parse_data->dbname, + parse_data->name, + new_element))) { DBUG_ASSERT(ret == OP_LOAD_ERROR); - my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0)); + delete new_element; } + else + event_queue->create_event(thd, new_element); } pthread_mutex_unlock(&LOCK_event_metadata); DBUG_RETURN(ret); + } @@ -388,6 +372,7 @@ bool Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to) { int ret; + Event_queue_element *new_element; DBUG_ENTER("Events::update_event"); LEX_STRING *new_dbname= rename_to ? &rename_to->m_db : NULL; LEX_STRING *new_name= rename_to ? &rename_to->m_name : NULL; @@ -401,12 +386,20 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to) /* On error conditions my_error() is called so no need to handle here */ if (!(ret= db_repository->update_event(thd, parse_data, new_dbname, new_name))) { - if ((ret= event_queue->update_event(thd, parse_data->dbname, - parse_data->name, new_dbname, new_name))) + LEX_STRING dbname= new_dbname ? *new_dbname : parse_data->dbname; + LEX_STRING name= new_name ? *new_name : parse_data->name; + + if (!(new_element= new Event_queue_element())) + ret= TRUE; // OOM + else if ((ret= db_repository->load_named_event(thd, dbname, name, + new_element))) { DBUG_ASSERT(ret == OP_LOAD_ERROR); - my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0)); + delete new_element; } + else + event_queue->update_event(thd, parse_data->dbname, parse_data->name, + new_element); } pthread_mutex_unlock(&LOCK_event_metadata); @@ -424,10 +417,6 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to) name [in] Event's name if_exists [in] When set and the event does not exist => warning onto the stack - only_from_disk [in] Whether to remove the event from the queue too. - In case of Event_job_data::drop() it's needed to - do only disk drop because Event_queue will handle - removal from memory queue. RETURN VALUE FALSE OK @@ -435,8 +424,7 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to) */ bool -Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists, - bool only_from_disk) +Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists) { int ret; DBUG_ENTER("Events::drop_event"); @@ -449,10 +437,7 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists, pthread_mutex_lock(&LOCK_event_metadata); /* On error conditions my_error() is called so no need to handle here */ if (!(ret= db_repository->drop_event(thd, dbname, name, if_exists))) - { - if (!only_from_disk) - event_queue->drop_event(thd, dbname, name); - } + event_queue->drop_event(thd, dbname, name); pthread_mutex_unlock(&LOCK_event_metadata); DBUG_RETURN(ret); } @@ -656,11 +641,12 @@ Events::init() } check_system_tables_error= FALSE; - if (event_queue->init_queue(thd, db_repository)) + if (event_queue->init_queue(thd) || load_events_from_db(thd)) { sql_print_error("SCHEDULER: Error while loading from disk."); goto end; } + scheduler->init_scheduler(event_queue); DBUG_ASSERT(opt_event_scheduler == Events::EVENTS_ON || @@ -668,6 +654,7 @@ Events::init() if (opt_event_scheduler == Events::EVENTS_ON) res= scheduler->start(); + Event_worker_thread::init(this, db_repository); end: delete thd; /* Remember that we don't have a THD */ @@ -858,7 +845,7 @@ Events::check_system_tables(THD *thd) bool ret= FALSE; DBUG_ENTER("Events::check_system_tables"); - DBUG_PRINT("enter", ("thd=0x%lx", thd)); + DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd)); thd->reset_n_backup_open_tables_state(&backup); @@ -904,3 +891,132 @@ Events::check_system_tables(THD *thd) DBUG_RETURN(ret); } + + +/* + Loads all ENABLED events from mysql.event into the prioritized + queue. Called during scheduler main thread initialization. Compiles + the events. Creates Event_queue_element instances for every ENABLED event + from mysql.event. + + SYNOPSIS + Events::load_events_from_db() + thd Thread context. Used for memory allocation in some cases. + + RETURN VALUE + 0 OK + !0 Error (EVEX_OPEN_TABLE_FAILED, EVEX_MICROSECOND_UNSUP, + EVEX_COMPILE_ERROR) - in all these cases mysql.event was + tampered. + + NOTES + Reports the error to the console +*/ + +int +Events::load_events_from_db(THD *thd) +{ + TABLE *table; + READ_RECORD read_record_info; + int ret= -1; + uint count= 0; + bool clean_the_queue= TRUE; + + DBUG_ENTER("Events::load_events_from_db"); + DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd)); + + if ((ret= db_repository->open_event_table(thd, TL_READ, &table))) + { + sql_print_error("SCHEDULER: Table mysql.event is damaged. Can not open"); + DBUG_RETURN(EVEX_OPEN_TABLE_FAILED); + } + + init_read_record(&read_record_info, thd, table ,NULL,1,0); + while (!(read_record_info.read_record(&read_record_info))) + { + Event_queue_element *et; + if (!(et= new Event_queue_element)) + { + DBUG_PRINT("info", ("Out of memory")); + break; + } + DBUG_PRINT("info", ("Loading event from row.")); + + if ((ret= et->load_from_row(table))) + { + sql_print_error("SCHEDULER: Error while loading from mysql.event. " + "Table probably corrupted"); + break; + } + if (et->status != Event_queue_element::ENABLED) + { + DBUG_PRINT("info",("%s is disabled",et->name.str)); + delete et; + continue; + } + + /* let's find when to be executed */ + if (et->compute_next_execution_time()) + { + sql_print_error("SCHEDULER: Error while computing execution time of %s.%s." + " Skipping", et->dbname.str, et->name.str); + continue; + } + + { + Event_job_data temp_job_data; + DBUG_PRINT("info", ("Event %s loaded from row. ", et->name.str)); + + temp_job_data.load_from_row(table); + + /* + We load only on scheduler root just to check whether the body + compiles. + */ + switch (ret= temp_job_data.compile(thd, thd->mem_root)) { + case EVEX_MICROSECOND_UNSUP: + sql_print_error("SCHEDULER: mysql.event is tampered. MICROSECOND is not " + "supported but found in mysql.event"); + break; + case EVEX_COMPILE_ERROR: + sql_print_error("SCHEDULER: Error while compiling %s.%s. Aborting load", + et->dbname.str, et->name.str); + break; + default: + break; + } + thd->end_statement(); + thd->cleanup_after_query(); + } + if (ret) + { + delete et; + goto end; + } + + DBUG_PRINT("load_events_from_db", ("Adding 0x%lx to the exec list.", + (long) et)); + event_queue->create_event(thd, et); + count++; + } + clean_the_queue= FALSE; +end: + end_read_record(&read_record_info); + + if (clean_the_queue) + { + event_queue->empty_queue(); + ret= -1; + } + else + { + ret= 0; + sql_print_information("SCHEDULER: Loaded %d event%s", count, + (count == 1)?"":"s"); + } + + close_thread_tables(thd); + + DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count)); + DBUG_RETURN(ret); +} diff --git a/sql/events.h b/sql/events.h index 79c4a419388..35ee3c569d0 100644 --- a/sql/events.h +++ b/sql/events.h @@ -4,8 +4,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -43,13 +42,6 @@ sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs); class Events { public: - /* - Quite NOT the best practice and will be removed once - Event_timed::drop() and Event_timed is fixed not do drop directly - or other scheme will be found. - */ - friend class Event_queue_element; - /* The order should match the order in opt_typelib */ enum enum_opt_event_scheduler { @@ -93,15 +85,11 @@ public: update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to); bool - drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists, - bool only_from_disk); + drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists); void drop_schema_events(THD *thd, char *db); - int - open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); - bool show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name); @@ -120,6 +108,9 @@ private: bool check_system_tables(THD *thd); + int + load_events_from_db(THD *thd); + /* Singleton DP is used */ Events(); ~Events(){} diff --git a/sql/examples/CMakeLists.txt b/sql/examples/CMakeLists.txt index d3cc430ef40..1a22e9a3efd 100755 --- a/sql/examples/CMakeLists.txt +++ b/sql/examples/CMakeLists.txt @@ -1,3 +1,18 @@ +# Copyright (C) 2006 MySQL 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 +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") diff --git a/sql/field.cc b/sql/field.cc index 3161026061f..488a5d591a3 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -47,6 +46,8 @@ uchar Field_null::null[1]={1}; const char field_separator=','; #define DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE 320 +#define LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE 128 +#define DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE 128 #define BLOB_PACK_LENGTH_TO_MAX_LENGH(arg) \ ((ulong) ((LL(1) << min(arg, 4) * 8) - LL(1))) @@ -1237,12 +1238,6 @@ Field::Field(char *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, } -uint Field::offset() -{ - return (uint) (ptr - (char*) table->record[0]); -} - - void Field::hash(ulong *nr, ulong *nr2) { if (is_null()) @@ -1420,13 +1415,14 @@ my_decimal* Field_num::val_decimal(my_decimal *decimal_value) Field_str::Field_str(char *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, - const char *field_name_arg, CHARSET_INFO *charset) + const char *field_name_arg, CHARSET_INFO *charset_arg) :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg) { - field_charset=charset; - if (charset->state & MY_CS_BINSORT) + field_charset= charset_arg; + if (charset_arg->state & MY_CS_BINSORT) flags|=BINARY_FLAG; + field_derivation= DERIVATION_IMPLICIT; } @@ -1531,7 +1527,7 @@ bool Field::get_time(TIME *ltime) Needs to be changed if/when we want to support different time formats */ -int Field::store_time(TIME *ltime, timestamp_type type) +int Field::store_time(TIME *ltime, timestamp_type type_arg) { ASSERT_COLUMN_MARKED_FOR_WRITE; char buff[MAX_DATE_STRING_REP_LENGTH]; @@ -1612,10 +1608,11 @@ void Field_null::sql_type(String &res) const This is an number stored as a pre-space (or pre-zero) string ****************************************************************************/ -void +int Field_decimal::reset(void) { Field_decimal::store(STRING_WITH_LEN("0"),&my_charset_bin); + return 0; } void Field_decimal::overflow(bool negative) @@ -2245,12 +2242,12 @@ Field_new_decimal::Field_new_decimal(char *ptr_arg, Field_new_decimal::Field_new_decimal(uint32 len_arg, - bool maybe_null, + bool maybe_null_arg, const char *name, uint8 dec_arg, bool unsigned_arg) :Field_num((char*) 0, len_arg, - maybe_null ? (uchar*) "": 0, 0, + maybe_null_arg ? (uchar*) "": 0, 0, NONE, name, dec_arg, 0, unsigned_arg) { precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg); @@ -2260,9 +2257,10 @@ Field_new_decimal::Field_new_decimal(uint32 len_arg, } -void Field_new_decimal::reset(void) +int Field_new_decimal::reset(void) { store_value(&decimal_zero); + return 0; } @@ -2353,7 +2351,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value) int Field_new_decimal::store(const char *from, uint length, - CHARSET_INFO *charset) + CHARSET_INFO *charset_arg) { ASSERT_COLUMN_MARKED_FOR_WRITE; int err; @@ -2362,7 +2360,7 @@ int Field_new_decimal::store(const char *from, uint length, if ((err= str2my_decimal(E_DEC_FATAL_ERROR & ~(E_DEC_OVERFLOW | E_DEC_BAD_NUM), - from, length, charset, &decimal_value)) && + from, length, charset_arg, &decimal_value)) && table->in_use->abort_on_warning) { /* Because "from" is not NUL-terminated and we use %s in the ER() */ @@ -2477,6 +2475,13 @@ int Field_new_decimal::store_decimal(const my_decimal *decimal_value) } +int Field_new_decimal::store_time(TIME *ltime, timestamp_type t_type) +{ + my_decimal decimal_value; + return store_value(date2my_decimal(ltime, &decimal_value)); +} + + double Field_new_decimal::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; @@ -2551,7 +2556,7 @@ uint Field_new_decimal::is_equal(create_field *new_field) (uint) (flags & UNSIGNED_FLAG)) && ((new_field->flags & AUTO_INCREMENT_FLAG) == (uint) (flags & AUTO_INCREMENT_FLAG)) && - (new_field->length == max_length()) && + (new_field->length == max_display_length()) && (new_field->decimals == dec)); } @@ -2563,7 +2568,6 @@ uint Field_new_decimal::is_equal(create_field *new_field) int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; - int not_used; // We can ignore result from str2int char *end; int error; @@ -2773,7 +2777,6 @@ void Field_tiny::sql_type(String &res) const int Field_short::store(const char *from,uint len,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; - int not_used; // We can ignore result from str2int char *end; int error; @@ -3060,7 +3063,6 @@ void Field_short::sql_type(String &res) const int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; - int not_used; // We can ignore result from str2int char *end; int error; @@ -3280,30 +3282,9 @@ void Field_medium::sql_type(String &res) const ** long int ****************************************************************************/ -/* - A helper function to check whether the next character - in the string "s" is MINUS SIGN. -*/ -#ifdef HAVE_CHARSET_ucs2 -static bool test_if_minus(CHARSET_INFO *cs, - const char *s, const char *e) -{ - my_wc_t wc; - return cs->cset->mb_wc(cs, &wc, (uchar*) s, (uchar*) e) > 0 && wc == '-'; -} -#else -/* - If not UCS2 support is compiled then it is easier -*/ -#define test_if_minus(cs, s, e) (*s == '-') -#endif - - int Field_long::store(const char *from,uint len,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; - ulong tmp_scan; - longlong tmp; long store_tmp; int error; char *end; @@ -4163,7 +4144,7 @@ int Field_double::store(double nr) else { double max_value; - if (dec >= NOT_FIXED_DEC) + if (not_fixed) { max_value= DBL_MAX; } @@ -4875,9 +4856,10 @@ int Field_time::store(const char *from,uint len,CHARSET_INFO *cs) { TIME ltime; long tmp; - int error; + int error= 0; + int warning; - if (str_to_time(from, len, <ime, &error)) + if (str_to_time(from, len, <ime, &warning)) { tmp=0L; error= 2; @@ -4886,40 +4868,38 @@ int Field_time::store(const char *from,uint len,CHARSET_INFO *cs) } else { - if (error) + if (warning & MYSQL_TIME_WARN_TRUNCATED) set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, from, len, MYSQL_TIMESTAMP_TIME, 1); - - if (ltime.month) - ltime.day=0; - tmp=(ltime.day*24L+ltime.hour)*10000L+(ltime.minute*100+ltime.second); - if (tmp > 8385959) + if (warning & MYSQL_TIME_WARN_OUT_OF_RANGE) { - tmp=8385959; set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, from, len, MYSQL_TIMESTAMP_TIME, !error); error= 1; } + if (ltime.month) + ltime.day=0; + tmp=(ltime.day*24L+ltime.hour)*10000L+(ltime.minute*100+ltime.second); if (error > 1) error= 2; } if (ltime.neg) tmp= -tmp; - error |= Field_time::store((longlong) tmp, FALSE); + int3store(ptr,tmp); return error; } -int Field_time::store_time(TIME *ltime, timestamp_type type) +int Field_time::store_time(TIME *ltime, timestamp_type time_type) { long tmp= ((ltime->month ? 0 : ltime->day * 24L) + ltime->hour) * 10000L + (ltime->minute * 100 + ltime->second); if (ltime->neg) tmp= -tmp; - return Field_time::store((longlong) tmp, TRUE); + return Field_time::store((longlong) tmp, FALSE); } @@ -4928,16 +4908,16 @@ int Field_time::store(double nr) ASSERT_COLUMN_MARKED_FOR_WRITE; long tmp; int error= 0; - if (nr > 8385959.0) + if (nr > (double)TIME_MAX_VALUE) { - tmp=8385959L; + tmp= TIME_MAX_VALUE; set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME); error= 1; } - else if (nr < -8385959.0) + else if (nr < (double)-TIME_MAX_VALUE) { - tmp= -8385959L; + tmp= -TIME_MAX_VALUE; set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME); error= 1; @@ -4966,17 +4946,17 @@ int Field_time::store(longlong nr, bool unsigned_val) ASSERT_COLUMN_MARKED_FOR_WRITE; long tmp; int error= 0; - if (nr < (longlong) -8385959L && !unsigned_val) + if (nr < (longlong) -TIME_MAX_VALUE && !unsigned_val) { - tmp= -8385959L; + tmp= -TIME_MAX_VALUE; set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME, 1); error= 1; } - else if (nr > (longlong) 8385959 || nr < 0 && unsigned_val) + else if (nr > (longlong) TIME_MAX_VALUE || nr < 0 && unsigned_val) { - tmp=8385959L; + tmp= TIME_MAX_VALUE; set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME, 1); @@ -5456,27 +5436,30 @@ int Field_newdate::store(const char *from,uint len,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; TIME l_time; - long tmp; int error; THD *thd= table ? table->in_use : current_thd; - if (str_to_datetime(from, len, &l_time, - (TIME_FUZZY_DATE | - (thd->variables.sql_mode & - (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | - MODE_INVALID_DATES))), - &error) <= MYSQL_TIMESTAMP_ERROR) + enum enum_mysql_timestamp_type ret; + if ((ret= str_to_datetime(from, len, &l_time, + (TIME_FUZZY_DATE | + (thd->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | + MODE_INVALID_DATES))), + &error)) <= MYSQL_TIMESTAMP_ERROR) { - tmp= 0L; + int3store(ptr,0L); error= 2; } else - tmp= l_time.day + l_time.month*32 + l_time.year*16*32; + { + int3store(ptr, l_time.day + l_time.month*32 + l_time.year*16*32); + if(!error && (ret != MYSQL_TIMESTAMP_DATE)) + return 2; + } if (error) set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, from, len, MYSQL_TIMESTAMP_DATE, 1); - int3store(ptr,tmp); return error; } @@ -5524,13 +5507,28 @@ int Field_newdate::store(longlong nr, bool unsigned_val) } -int Field_newdate::store_time(TIME *ltime,timestamp_type type) +int Field_newdate::store_time(TIME *ltime,timestamp_type time_type) { ASSERT_COLUMN_MARKED_FOR_WRITE; long tmp; int error= 0; - if (type == MYSQL_TIMESTAMP_DATE || type == MYSQL_TIMESTAMP_DATETIME) + if (time_type == MYSQL_TIMESTAMP_DATE || + time_type == MYSQL_TIMESTAMP_DATETIME) + { tmp=ltime->year*16*32+ltime->month*32+ltime->day; + if (check_date(ltime, tmp != 0, + (TIME_FUZZY_DATE | + (current_thd->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | + MODE_INVALID_DATES))), &error)) + { + char buff[12]; + String str(buff, sizeof(buff), &my_charset_latin1); + make_date((DATE_TIME_FORMAT *) 0, ltime, &str); + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, + str.ptr(), str.length(), MYSQL_TIMESTAMP_DATE, 1); + } + } else { tmp=0; @@ -5735,7 +5733,7 @@ int Field_datetime::store(longlong nr, bool unsigned_val) } -int Field_datetime::store_time(TIME *ltime,timestamp_type type) +int Field_datetime::store_time(TIME *ltime,timestamp_type time_type) { ASSERT_COLUMN_MARKED_FOR_WRITE; longlong tmp; @@ -5744,9 +5742,24 @@ int Field_datetime::store_time(TIME *ltime,timestamp_type type) We don't perform range checking here since values stored in TIME structure always fit into DATETIME range. */ - if (type == MYSQL_TIMESTAMP_DATE || type == MYSQL_TIMESTAMP_DATETIME) + if (time_type == MYSQL_TIMESTAMP_DATE || + time_type == MYSQL_TIMESTAMP_DATETIME) + { tmp=((ltime->year*10000L+ltime->month*100+ltime->day)*LL(1000000)+ (ltime->hour*10000L+ltime->minute*100+ltime->second)); + if (check_date(ltime, tmp != 0, + (TIME_FUZZY_DATE | + (current_thd->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | + MODE_INVALID_DATES))), &error)) + { + char buff[19]; + String str(buff, sizeof(buff), &my_charset_latin1); + make_datetime((DATE_TIME_FORMAT *) 0, ltime, &str); + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, + str.ptr(), str.length(), MYSQL_TIMESTAMP_DATETIME,1); + } + } else { tmp=0; @@ -6109,32 +6122,32 @@ int Field_str::store(double nr) char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE]; uint length; bool use_scientific_notation= TRUE; - uint char_length= field_length / charset()->mbmaxlen; + uint local_char_length= field_length / charset()->mbmaxlen; /* Check fabs(nr) against longest value that can be stored in field, which depends on whether the value is < 1 or not, and negative or not */ double anr= fabs(nr); int neg= (nr < 0.0) ? 1 : 0; - if (char_length > 4 && char_length < 32 && - (anr < 1.0 ? anr > 1/(log_10[max(0,(int) char_length-neg-2)]) /* -2 for "0." */ - : anr < log_10[char_length-neg]-1)) + if (local_char_length > 4 && local_char_length < 32 && + (anr < 1.0 ? anr > 1/(log_10[max(0,(int) local_char_length-neg-2)]) /* -2 for "0." */ + : anr < log_10[local_char_length-neg]-1)) use_scientific_notation= FALSE; length= (uint) my_sprintf(buff, (buff, "%-.*g", (use_scientific_notation ? - max(0, (int)char_length-neg-5) : - char_length), + max(0, (int)local_char_length-neg-5) : + local_char_length), nr)); /* +1 below is because "precision" in %g above means the max. number of significant digits, not the output width. Thus the width can be larger than number of significant digits by 1 (for decimal point) - the test for char_length < 5 is for extreme cases, + the test for local_char_length < 5 is for extreme cases, like inserting 500.0 in char(1) */ - DBUG_ASSERT(char_length < 5 || length <= char_length+1); + DBUG_ASSERT(local_char_length < 5 || length <= local_char_length+1); return store((const char *) buff, length, charset()); } @@ -6155,7 +6168,7 @@ uint Field_str::is_equal(create_field *new_field) return ((new_field->sql_type == real_type()) && new_field->charset == field_charset && - new_field->length == max_length()); + new_field->length == max_display_length()); } @@ -6182,20 +6195,50 @@ int Field_longstr::store_decimal(const my_decimal *d) double Field_string::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; - int not_used; - char *end_not_used; + int error; + char *end; CHARSET_INFO *cs= charset(); - return my_strntod(cs,ptr,field_length,&end_not_used,¬_used); + double result; + + result= my_strntod(cs,ptr,field_length,&end,&error); + if (!table->in_use->no_errors && + (error || (field_length != (uint32)(end - ptr) && + !check_if_only_end_space(cs, end, ptr + field_length)))) + { + char buf[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE]; + String tmp(buf, sizeof(buf), cs); + tmp.copy(ptr, field_length, cs); + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TRUNCATED_WRONG_VALUE, + ER(ER_TRUNCATED_WRONG_VALUE), + "DOUBLE", tmp.c_ptr()); + } + return result; } longlong Field_string::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; - int not_used; - char *end_not_used; - CHARSET_INFO *cs=charset(); - return my_strntoll(cs,ptr,field_length,10,&end_not_used,¬_used); + int error; + char *end; + CHARSET_INFO *cs= charset(); + longlong result; + + result= my_strntoll(cs,ptr,field_length,10,&end,&error); + if (!table->in_use->no_errors && + (error || (field_length != (uint32)(end - ptr) && + !check_if_only_end_space(cs, end, ptr + field_length)))) + { + char buf[LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE]; + String tmp(buf, sizeof(buf), cs); + tmp.copy(ptr, field_length, cs); + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TRUNCATED_WRONG_VALUE, + ER(ER_TRUNCATED_WRONG_VALUE), + "INTEGER", tmp.c_ptr()); + } + return result; } @@ -6214,8 +6257,20 @@ String *Field_string::val_str(String *val_buffer __attribute__((unused)), my_decimal *Field_string::val_decimal(my_decimal *decimal_value) { ASSERT_COLUMN_MARKED_FOR_READ; - str2my_decimal(E_DEC_FATAL_ERROR, ptr, field_length, charset(), + int err= str2my_decimal(E_DEC_FATAL_ERROR, ptr, field_length, charset(), decimal_value); + if (!table->in_use->no_errors && err) + { + char buf[DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE]; + CHARSET_INFO *cs= charset(); + String tmp(buf, sizeof(buf), cs); + tmp.copy(ptr, field_length, cs); + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TRUNCATED_WRONG_VALUE, + ER(ER_TRUNCATED_WRONG_VALUE), + "DECIMAL", tmp.c_ptr()); + } + return decimal_value; } @@ -6245,9 +6300,9 @@ int Field_string::cmp(const char *a_ptr, const char *b_ptr) void Field_string::sort_string(char *to,uint length) { - uint tmp= my_strnxfrm(field_charset, - (uchar*) to, length, - (uchar*) ptr, field_length); + IF_DBUG(uint tmp=) my_strnxfrm(field_charset, + (uchar*) to, length, + (uchar*) ptr, field_length); DBUG_ASSERT(tmp == length); } @@ -6275,10 +6330,11 @@ void Field_string::sql_type(String &res) const char *Field_string::pack(char *to, const char *from, uint max_length) { uint length= min(field_length,max_length); - uint char_length= max_length/field_charset->mbmaxlen; - if (length > char_length) - char_length= my_charpos(field_charset, from, from+length, char_length); - set_if_smaller(length, char_length); + uint local_char_length= max_length/field_charset->mbmaxlen; + if (length > local_char_length) + local_char_length= my_charpos(field_charset, from, from+length, + local_char_length); + set_if_smaller(length, local_char_length); while (length && from[length-1] == ' ') length--; *to++= (char) (uchar) length; @@ -6362,15 +6418,15 @@ int Field_string::pack_cmp(const char *a, const char *b, uint length, int Field_string::pack_cmp(const char *key, uint length, my_bool insert_or_update) { - uint row_length, key_length; + uint row_length, local_key_length; char *end; if (length > 255) { - key_length= uint2korr(key); + local_key_length= uint2korr(key); key+= 2; } else - key_length= (uint) (uchar) *key++; + local_key_length= (uint) (uchar) *key++; /* Only use 'length' of key, not field_length */ end= ptr + length; @@ -6380,7 +6436,7 @@ int Field_string::pack_cmp(const char *key, uint length, return field_charset->coll->strnncollsp(field_charset, (const uchar*) ptr, row_length, - (const uchar*) key, key_length, + (const uchar*) key, local_key_length, insert_or_update); } @@ -6572,11 +6628,11 @@ int Field_varstring::cmp_max(const char *a_ptr, const char *b_ptr, int Field_varstring::key_cmp(const byte *key_ptr, uint max_key_length) { uint length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr); - uint char_length= max_key_length / field_charset->mbmaxlen; + uint local_char_length= max_key_length / field_charset->mbmaxlen; - char_length= my_charpos(field_charset, ptr + length_bytes, - ptr + length_bytes + length, char_length); - set_if_smaller(length, char_length); + local_char_length= my_charpos(field_charset, ptr + length_bytes, + ptr + length_bytes + length, local_char_length); + set_if_smaller(length, local_char_length); return field_charset->coll->strnncollsp(field_charset, (const uchar*) ptr + length_bytes, length, @@ -6658,9 +6714,9 @@ void Field_varstring::sql_type(String &res) const } -uint32 Field_varstring::data_length(const char *from) +uint32 Field_varstring::data_length() { - return length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr); + return length_bytes == 1 ? (uint32) (uchar) *ptr : uint2korr(ptr); } /* @@ -6686,13 +6742,14 @@ char *Field_varstring::pack(char *to, const char *from, uint max_length) char *Field_varstring::pack_key(char *to, const char *key, uint max_length) { uint length= length_bytes == 1 ? (uint) (uchar) *key : uint2korr(key); - uint char_length= ((field_charset->mbmaxlen > 1) ? + uint local_char_length= ((field_charset->mbmaxlen > 1) ? max_length/field_charset->mbmaxlen : max_length); key+= length_bytes; - if (length > char_length) + if (length > local_char_length) { - char_length= my_charpos(field_charset, key, key+length, char_length); - set_if_smaller(length, char_length); + local_char_length= my_charpos(field_charset, key, key+length, + local_char_length); + set_if_smaller(length, local_char_length); } *to++= (char) (length & 255); if (max_length > 255) @@ -6788,11 +6845,12 @@ const char *Field_varstring::unpack(char *to, const char *from) } -int Field_varstring::pack_cmp(const char *a, const char *b, uint key_length, +int Field_varstring::pack_cmp(const char *a, const char *b, + uint key_length_arg, my_bool insert_or_update) { uint a_length, b_length; - if (key_length > 255) + if (key_length_arg > 255) { a_length=uint2korr(a); a+= 2; b_length=uint2korr(b); b+= 2; @@ -6809,26 +6867,28 @@ int Field_varstring::pack_cmp(const char *a, const char *b, uint key_length, } -int Field_varstring::pack_cmp(const char *b, uint key_length, +int Field_varstring::pack_cmp(const char *b, uint key_length_arg, my_bool insert_or_update) { char *a= ptr+ length_bytes; uint a_length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr); uint b_length; - uint char_length= ((field_charset->mbmaxlen > 1) ? - key_length / field_charset->mbmaxlen : key_length); + uint local_char_length= ((field_charset->mbmaxlen > 1) ? + key_length_arg / field_charset->mbmaxlen : + key_length_arg); - if (key_length > 255) + if (key_length_arg > 255) { b_length=uint2korr(b); b+= HA_KEY_BLOB_LENGTH; } else b_length= (uint) (uchar) *b++; - if (a_length > char_length) + if (a_length > local_char_length) { - char_length= my_charpos(field_charset, a, a+a_length, char_length); - set_if_smaller(a_length, char_length); + local_char_length= my_charpos(field_charset, a, a+a_length, + local_char_length); + set_if_smaller(a_length, local_char_length); } return field_charset->coll->strnncollsp(field_charset, @@ -6853,13 +6913,15 @@ uint Field_varstring::max_packed_col_length(uint max_length) } -void Field_varstring::get_key_image(char *buff, uint length, imagetype type) +void Field_varstring::get_key_image(char *buff, uint length, + imagetype type_arg) { uint f_length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr); - uint char_length= length / field_charset->mbmaxlen; + uint local_char_length= length / field_charset->mbmaxlen; char *pos= ptr+length_bytes; - char_length= my_charpos(field_charset, pos, pos + f_length, char_length); - set_if_smaller(f_length, char_length); + local_char_length= my_charpos(field_charset, pos, pos + f_length, + local_char_length); + set_if_smaller(f_length, local_char_length); /* Key is always stored with 2 bytes */ int2store(buff,f_length); memcpy(buff+HA_KEY_BLOB_LENGTH, pos, f_length); @@ -6940,11 +7002,11 @@ uint Field_varstring::is_equal(create_field *new_field) if (new_field->sql_type == real_type() && new_field->charset == field_charset) { - if (new_field->length == max_length()) + if (new_field->length == max_display_length()) return IS_EQUAL_YES; - if (new_field->length > max_length() && - ((new_field->length <= 255 && max_length() <= 255) || - (new_field->length > 255 && max_length() > 255))) + if (new_field->length > max_display_length() && + ((new_field->length <= 255 && max_display_length() <= 255) || + (new_field->length > 255 && max_display_length() > 255))) return IS_EQUAL_PACK_LENGTH; // VARCHAR, longer variable length } return IS_EQUAL_NO; @@ -7144,7 +7206,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) cannot_convert_error_pos, from + length)) return 2; - if (copy_length < length) + if (from_end_pos < from + length) { report_data_too_long(this); return 2; @@ -7275,13 +7337,13 @@ int Field_blob::cmp_binary(const char *a_ptr, const char *b_ptr, /* The following is used only when comparing a key */ -void Field_blob::get_key_image(char *buff, uint length, imagetype type) +void Field_blob::get_key_image(char *buff, uint length, imagetype type_arg) { uint32 blob_length= get_length(ptr); char *blob; #ifdef HAVE_SPATIAL - if (type == itMBR) + if (type_arg == itMBR) { const char *dummy; MBR mbr; @@ -7309,10 +7371,10 @@ void Field_blob::get_key_image(char *buff, uint length, imagetype type) #endif /*HAVE_SPATIAL*/ get_ptr(&blob); - uint char_length= length / field_charset->mbmaxlen; - char_length= my_charpos(field_charset, blob, blob + blob_length, - char_length); - set_if_smaller(blob_length, char_length); + uint local_char_length= length / field_charset->mbmaxlen; + local_char_length= my_charpos(field_charset, blob, blob + blob_length, + local_char_length); + set_if_smaller(blob_length, local_char_length); if ((uint32) length > blob_length) { @@ -7341,9 +7403,10 @@ int Field_blob::key_cmp(const byte *key_ptr, uint max_key_length) uint blob_length=get_length(ptr); memcpy_fixed(&blob1,ptr+packlength,sizeof(char*)); CHARSET_INFO *cs= charset(); - uint char_length= max_key_length / cs->mbmaxlen; - char_length= my_charpos(cs, blob1, blob1+blob_length, char_length); - set_if_smaller(blob_length, char_length); + uint local_char_length= max_key_length / cs->mbmaxlen; + local_char_length= my_charpos(cs, blob1, blob1+blob_length, + local_char_length); + set_if_smaller(blob_length, local_char_length); return Field_blob::cmp(blob1, blob_length, (char*) key_ptr+HA_KEY_BLOB_LENGTH, uint2korr(key_ptr)); @@ -7465,11 +7528,11 @@ const char *Field_blob::unpack(char *to, const char *from) /* Keys for blobs are like keys on varchars */ -int Field_blob::pack_cmp(const char *a, const char *b, uint key_length, +int Field_blob::pack_cmp(const char *a, const char *b, uint key_length_arg, my_bool insert_or_update) { uint a_length, b_length; - if (key_length > 255) + if (key_length_arg > 255) { a_length=uint2korr(a); a+=2; b_length=uint2korr(b); b+=2; @@ -7486,19 +7549,19 @@ int Field_blob::pack_cmp(const char *a, const char *b, uint key_length, } -int Field_blob::pack_cmp(const char *b, uint key_length, +int Field_blob::pack_cmp(const char *b, uint key_length_arg, my_bool insert_or_update) { char *a; + uint a_length, b_length; memcpy_fixed(&a,ptr+packlength,sizeof(char*)); if (!a) - return key_length > 0 ? -1 : 0; - uint a_length=get_length(ptr); - uint b_length; + return key_length_arg > 0 ? -1 : 0; - if (key_length > 255) + a_length= get_length(ptr); + if (key_length_arg > 255) { - b_length=uint2korr(b); b+=2; + b_length= uint2korr(b); b+=2; } else b_length= (uint) (uchar) *b++; @@ -7515,13 +7578,14 @@ char *Field_blob::pack_key(char *to, const char *from, uint max_length) char *save=ptr; ptr=(char*) from; uint32 length=get_length(); // Length of from string - uint char_length= ((field_charset->mbmaxlen > 1) ? + uint local_char_length= ((field_charset->mbmaxlen > 1) ? max_length/field_charset->mbmaxlen : max_length); if (length) get_ptr((char**) &from); - if (length > char_length) - char_length= my_charpos(field_charset, from, from+length, char_length); - set_if_smaller(length, char_length); + if (length > local_char_length) + local_char_length= my_charpos(field_charset, from, from+length, + local_char_length); + set_if_smaller(length, local_char_length); *to++= (uchar) length; if (max_length > 255) // 2 byte length *to++= (uchar) (length >> 8); @@ -7608,7 +7672,7 @@ uint Field_blob::max_packed_col_length(uint max_length) #ifdef HAVE_SPATIAL -void Field_geom::get_key_image(char *buff, uint length, imagetype type) +void Field_geom::get_key_image(char *buff, uint length, imagetype type_arg) { char *blob; const char *dummy; @@ -7706,7 +7770,7 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs) goto err; wkb_type= uint4korr(from + SRID_SIZE + 1); if (wkb_type < (uint32) Geometry::wkb_point || - wkb_type > (uint32) Geometry::wkb_end) + wkb_type > (uint32) Geometry::wkb_last) goto err; Field_blob::store_length(length); if (table->copy_blobs || length <= MAX_FIELD_WIDTH) @@ -7961,6 +8025,16 @@ void Field_enum::sql_type(String &res) const } +Field *Field_enum::new_field(MEM_ROOT *root, struct st_table *new_table, + bool keep_type) +{ + Field_enum *res= (Field_enum*) Field::new_field(root, new_table, keep_type); + if (res) + res->typelib= copy_typelib(root, typelib); + return res; +} + + /* set type. This is a string which can have a collection of different values. @@ -8125,7 +8199,7 @@ uint Field_num::is_equal(create_field *new_field) UNSIGNED_FLAG)) && ((new_field->flags & AUTO_INCREMENT_FLAG) == (uint) (flags & AUTO_INCREMENT_FLAG)) && - (new_field->length <= max_length())); + (new_field->length <= max_display_length())); } @@ -8166,6 +8240,7 @@ Field_bit::Field_bit(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, bit_ptr(bit_ptr_arg), bit_ofs(bit_ofs_arg), bit_len(len_arg & 7), bytes_in_rec(len_arg / 8) { + flags|= UNSIGNED_FLAG; /* Ensure that Field::eq() can distinguish between two different bit fields. (two bit fields that are not null, may have same ptr and null_ptr) @@ -8186,8 +8261,8 @@ Field_bit::do_last_null_byte() const bits. On systems with CHAR_BIT > 8 (not very common), the storage will lose the extra bits. */ - DBUG_PRINT("debug", ("bit_ofs=%d, bit_len=%d, bit_ptr=%p", - bit_ofs, bit_len, bit_ptr)); + DBUG_PRINT("test", ("bit_ofs: %d, bit_len: %d bit_ptr: 0x%lx", + bit_ofs, bit_len, (long) bit_ptr)); uchar *result; if (bit_len == 0) result= null_ptr; @@ -8405,7 +8480,7 @@ int Field_bit::cmp_offset(uint row_offset) } -void Field_bit::get_key_image(char *buff, uint length, imagetype type) +void Field_bit::get_key_image(char *buff, uint length, imagetype type_arg) { if (bit_len) { @@ -8455,9 +8530,12 @@ const char *Field_bit::unpack(char *to, const char *from) void Field_bit::set_default() { - my_ptrdiff_t const offset= table->s->default_values - table->record[0]; - uchar bits= get_rec_bits(bit_ptr + offset, bit_ofs, bit_len); - set_rec_bits(bits, bit_ptr, bit_ofs, bit_len); + if (bit_len > 0) + { + my_ptrdiff_t const offset= table->s->default_values - table->record[0]; + uchar bits= get_rec_bits(bit_ptr + offset, bit_ofs, bit_len); + set_rec_bits(bits, bit_ptr, bit_ofs, bit_len); + } Field::set_default(); } @@ -8472,6 +8550,7 @@ Field_bit_as_char::Field_bit_as_char(char *ptr_arg, uint32 len_arg, :Field_bit(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, 0, 0, unireg_check_arg, field_name_arg) { + flags|= UNSIGNED_FLAG; bit_len= 0; bytes_in_rec= (len_arg + 7) / 8; } @@ -8481,7 +8560,7 @@ int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; int delta; - uchar bits= field_length & 7; + uchar bits= (uchar) (field_length & 7); for (; length && !*from; from++, length--); // skip left 0's delta= bytes_in_rec - length; @@ -8575,7 +8654,7 @@ void create_field::create_length_to_internal_length(void) void create_field::init_for_tmp_table(enum_field_types sql_type_arg, - uint32 length_arg, uint32 decimals, + uint32 length_arg, uint32 decimals_arg, bool maybe_null, bool is_unsigned) { field_name= ""; @@ -8586,7 +8665,7 @@ void create_field::init_for_tmp_table(enum_field_types sql_type_arg, charset= &my_charset_bin; geom_type= Field::GEOM_GEOMETRY; pack_flag= (FIELDFLAG_NUMBER | - ((decimals & FIELDFLAG_MAX_DEC) << FIELDFLAG_DEC_SHIFT) | + ((decimals_arg & FIELDFLAG_MAX_DEC) << FIELDFLAG_DEC_SHIFT) | (maybe_null ? FIELDFLAG_MAYBE_NULL : 0) | (is_unsigned ? 0 : FIELDFLAG_DECIMAL)); } @@ -8656,7 +8735,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, it is NOT NULL, not an AUTO_INCREMENT field and not a TIMESTAMP. */ if (!fld_default_value && !(fld_type_modifier & AUTO_INCREMENT_FLAG) && - (fld_type_modifier & NOT_NULL_FLAG) && fld_type != FIELD_TYPE_TIMESTAMP) + (fld_type_modifier & NOT_NULL_FLAG) && fld_type != MYSQL_TYPE_TIMESTAMP) flags|= NO_DEFAULT_VALUE_FLAG; if (fld_length && !(length= (uint) atoi(fld_length))) @@ -8664,34 +8743,34 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, sign_len= fld_type_modifier & UNSIGNED_FLAG ? 0 : 1; switch (fld_type) { - case FIELD_TYPE_TINY: + case MYSQL_TYPE_TINY: if (!fld_length) length= MAX_TINYINT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; - case FIELD_TYPE_SHORT: + case MYSQL_TYPE_SHORT: if (!fld_length) length= MAX_SMALLINT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; - case FIELD_TYPE_INT24: + case MYSQL_TYPE_INT24: if (!fld_length) length= MAX_MEDIUMINT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; - case FIELD_TYPE_LONG: + case MYSQL_TYPE_LONG: if (!fld_length) length= MAX_INT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; - case FIELD_TYPE_LONGLONG: + case MYSQL_TYPE_LONGLONG: if (!fld_length) length= MAX_BIGINT_WIDTH; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; - case FIELD_TYPE_NULL: + case MYSQL_TYPE_NULL: break; - case FIELD_TYPE_NEWDECIMAL: + case MYSQL_TYPE_NEWDECIMAL: if (!fld_length && !decimals) length= 10; if (length > DECIMAL_MAX_PRECISION) @@ -8720,11 +8799,11 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, break; case MYSQL_TYPE_STRING: break; - case FIELD_TYPE_BLOB: - case FIELD_TYPE_TINY_BLOB: - case FIELD_TYPE_LONG_BLOB: - case FIELD_TYPE_MEDIUM_BLOB: - case FIELD_TYPE_GEOMETRY: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_GEOMETRY: if (fld_default_value) { /* Allow empty as default value. */ @@ -8756,12 +8835,12 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, } flags|= BLOB_FLAG; break; - case FIELD_TYPE_YEAR: + case MYSQL_TYPE_YEAR: if (!fld_length || length != 2) length= 4; /* Default length */ flags|= ZEROFILL_FLAG | UNSIGNED_FLAG; break; - case FIELD_TYPE_FLOAT: + case MYSQL_TYPE_FLOAT: /* change FLOAT(precision) to FLOAT or DOUBLE */ allowed_type_modifier= AUTO_INCREMENT_FLAG; if (fld_length && !fld_decimals) @@ -8774,7 +8853,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, } else if (tmp_length > PRECISION_FOR_FLOAT) { - sql_type= FIELD_TYPE_DOUBLE; + sql_type= MYSQL_TYPE_DOUBLE; length= DBL_DIG+7; /* -[digits].E+### */ } else @@ -8794,7 +8873,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, DBUG_RETURN(TRUE); } break; - case FIELD_TYPE_DOUBLE: + case MYSQL_TYPE_DOUBLE: allowed_type_modifier= AUTO_INCREMENT_FLAG; if (!fld_length && !fld_decimals) { @@ -8808,7 +8887,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, DBUG_RETURN(TRUE); } break; - case FIELD_TYPE_TIMESTAMP: + case MYSQL_TYPE_TIMESTAMP: if (!fld_length) length= 14; /* Full date YYYYMMDDHHMMSS */ else if (length != 19) @@ -8859,21 +8938,21 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, Field::NONE)); } break; - case FIELD_TYPE_DATE: + case MYSQL_TYPE_DATE: /* Old date type. */ if (protocol_version != PROTOCOL_VERSION-1) - sql_type= FIELD_TYPE_NEWDATE; + sql_type= MYSQL_TYPE_NEWDATE; /* fall trough */ - case FIELD_TYPE_NEWDATE: + case MYSQL_TYPE_NEWDATE: length= 10; break; - case FIELD_TYPE_TIME: + case MYSQL_TYPE_TIME: length= 10; break; - case FIELD_TYPE_DATETIME: + case MYSQL_TYPE_DATETIME: length= 19; break; - case FIELD_TYPE_SET: + case MYSQL_TYPE_SET: { if (fld_interval_list->elements > sizeof(longlong)*8) { @@ -8894,7 +8973,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, length= 1; break; } - case FIELD_TYPE_ENUM: + case MYSQL_TYPE_ENUM: { /* Should be safe. */ pack_length= get_enum_pack_length(fld_interval_list->elements); @@ -8903,7 +8982,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, String *tmp; while ((tmp= it++)) interval_list.push_back(tmp); - length= 1; /* See comment for FIELD_TYPE_SET above. */ + length= 1; /* See comment for MYSQL_TYPE_SET above. */ break; } case MYSQL_TYPE_VAR_STRING: @@ -8922,19 +9001,19 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, pack_length= (length + 7) / 8; break; } - case FIELD_TYPE_DECIMAL: + case MYSQL_TYPE_DECIMAL: DBUG_ASSERT(0); /* Was obsolete */ } /* Remember the value of length */ char_length= length; if (!(flags & BLOB_FLAG) && - ((length > max_field_charlength && fld_type != FIELD_TYPE_SET && - fld_type != FIELD_TYPE_ENUM && + ((length > max_field_charlength && fld_type != MYSQL_TYPE_SET && + fld_type != MYSQL_TYPE_ENUM && (fld_type != MYSQL_TYPE_VARCHAR || fld_default_value)) || (!length && fld_type != MYSQL_TYPE_STRING && - fld_type != MYSQL_TYPE_VARCHAR && fld_type != FIELD_TYPE_GEOMETRY))) + fld_type != MYSQL_TYPE_VARCHAR && fld_type != MYSQL_TYPE_GEOMETRY))) { my_error((fld_type == MYSQL_TYPE_VAR_STRING || fld_type == MYSQL_TYPE_VARCHAR || @@ -8959,13 +9038,13 @@ enum_field_types get_blob_type_from_length(ulong length) { enum_field_types type; if (length < 256) - type= FIELD_TYPE_TINY_BLOB; + type= MYSQL_TYPE_TINY_BLOB; else if (length < 65536) - type= FIELD_TYPE_BLOB; + type= MYSQL_TYPE_BLOB; else if (length < 256L*256L*256L) - type= FIELD_TYPE_MEDIUM_BLOB; + type= MYSQL_TYPE_MEDIUM_BLOB; else - type= FIELD_TYPE_LONG_BLOB; + type= MYSQL_TYPE_LONG_BLOB; return type; } @@ -8979,32 +9058,32 @@ uint32 calc_pack_length(enum_field_types type,uint32 length) switch (type) { case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: - case FIELD_TYPE_DECIMAL: return (length); + case MYSQL_TYPE_DECIMAL: return (length); case MYSQL_TYPE_VARCHAR: return (length + (length < 256 ? 1: 2)); - case FIELD_TYPE_YEAR: - case FIELD_TYPE_TINY : return 1; - case FIELD_TYPE_SHORT : return 2; - case FIELD_TYPE_INT24: - case FIELD_TYPE_NEWDATE: - case FIELD_TYPE_TIME: return 3; - case FIELD_TYPE_TIMESTAMP: - case FIELD_TYPE_DATE: - case FIELD_TYPE_LONG : return 4; - case FIELD_TYPE_FLOAT : return sizeof(float); - case FIELD_TYPE_DOUBLE: return sizeof(double); - case FIELD_TYPE_DATETIME: - case FIELD_TYPE_LONGLONG: return 8; /* Don't crash if no longlong */ - case FIELD_TYPE_NULL : return 0; - case FIELD_TYPE_TINY_BLOB: return 1+portable_sizeof_char_ptr; - case FIELD_TYPE_BLOB: return 2+portable_sizeof_char_ptr; - case FIELD_TYPE_MEDIUM_BLOB: return 3+portable_sizeof_char_ptr; - case FIELD_TYPE_LONG_BLOB: return 4+portable_sizeof_char_ptr; - case FIELD_TYPE_GEOMETRY: return 4+portable_sizeof_char_ptr; - case FIELD_TYPE_SET: - case FIELD_TYPE_ENUM: - case FIELD_TYPE_NEWDECIMAL: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_TINY : return 1; + case MYSQL_TYPE_SHORT : return 2; + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_TIME: return 3; + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_LONG : return 4; + case MYSQL_TYPE_FLOAT : return sizeof(float); + case MYSQL_TYPE_DOUBLE: return sizeof(double); + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_LONGLONG: return 8; /* Don't crash if no longlong */ + case MYSQL_TYPE_NULL : return 0; + case MYSQL_TYPE_TINY_BLOB: return 1+portable_sizeof_char_ptr; + case MYSQL_TYPE_BLOB: return 2+portable_sizeof_char_ptr; + case MYSQL_TYPE_MEDIUM_BLOB: return 3+portable_sizeof_char_ptr; + case MYSQL_TYPE_LONG_BLOB: return 4+portable_sizeof_char_ptr; + case MYSQL_TYPE_GEOMETRY: return 4+portable_sizeof_char_ptr; + case MYSQL_TYPE_SET: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_NEWDECIMAL: abort(); return 0; // This shouldn't happen - case FIELD_TYPE_BIT: return length / 8; + case MYSQL_TYPE_BIT: return length / 8; default: return 0; } @@ -9014,11 +9093,11 @@ uint32 calc_pack_length(enum_field_types type,uint32 length) uint pack_length_to_packflag(uint type) { switch (type) { - case 1: return f_settype((uint) FIELD_TYPE_TINY); - case 2: return f_settype((uint) FIELD_TYPE_SHORT); - case 3: return f_settype((uint) FIELD_TYPE_INT24); - case 4: return f_settype((uint) FIELD_TYPE_LONG); - case 8: return f_settype((uint) FIELD_TYPE_LONGLONG); + case 1: return f_settype((uint) MYSQL_TYPE_TINY); + case 2: return f_settype((uint) MYSQL_TYPE_SHORT); + case 3: return f_settype((uint) MYSQL_TYPE_INT24); + case 4: return f_settype((uint) MYSQL_TYPE_LONG); + case 8: return f_settype((uint) MYSQL_TYPE_LONGLONG); } return 0; // This shouldn't happen } @@ -9038,7 +9117,7 @@ Field *make_field(TABLE_SHARE *share, char *ptr, uint32 field_length, uchar bit_offset; LINT_INIT(bit_ptr); LINT_INIT(bit_offset); - if (field_type == FIELD_TYPE_BIT && !f_bit_as_char(pack_flag)) + if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) { bit_ptr= null_pos; bit_offset= null_bit; @@ -9060,11 +9139,11 @@ Field *make_field(TABLE_SHARE *share, char *ptr, uint32 field_length, } switch (field_type) { - case FIELD_TYPE_DATE: - case FIELD_TYPE_NEWDATE: - case FIELD_TYPE_TIME: - case FIELD_TYPE_DATETIME: - case FIELD_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: field_charset= &my_charset_bin; default: break; } @@ -9074,7 +9153,7 @@ Field *make_field(TABLE_SHARE *share, char *ptr, uint32 field_length, if (!f_is_packed(pack_flag)) { if (field_type == MYSQL_TYPE_STRING || - field_type == FIELD_TYPE_DECIMAL || // 3.23 or 4.0 string + field_type == MYSQL_TYPE_DECIMAL || // 3.23 or 4.0 string field_type == MYSQL_TYPE_VAR_STRING) return new Field_string(ptr,field_length,null_pos,null_bit, unireg_check, field_name, @@ -9117,78 +9196,78 @@ Field *make_field(TABLE_SHARE *share, char *ptr, uint32 field_length, } switch (field_type) { - case FIELD_TYPE_DECIMAL: + case MYSQL_TYPE_DECIMAL: return new Field_decimal(ptr,field_length,null_pos,null_bit, unireg_check, field_name, f_decimals(pack_flag), f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); - case FIELD_TYPE_NEWDECIMAL: + case MYSQL_TYPE_NEWDECIMAL: return new Field_new_decimal(ptr,field_length,null_pos,null_bit, unireg_check, field_name, f_decimals(pack_flag), f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); - case FIELD_TYPE_FLOAT: + case MYSQL_TYPE_FLOAT: return new Field_float(ptr,field_length,null_pos,null_bit, unireg_check, field_name, f_decimals(pack_flag), f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag)== 0); - case FIELD_TYPE_DOUBLE: + case MYSQL_TYPE_DOUBLE: return new Field_double(ptr,field_length,null_pos,null_bit, unireg_check, field_name, f_decimals(pack_flag), f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag)== 0); - case FIELD_TYPE_TINY: + case MYSQL_TYPE_TINY: return new Field_tiny(ptr,field_length,null_pos,null_bit, unireg_check, field_name, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); - case FIELD_TYPE_SHORT: + case MYSQL_TYPE_SHORT: return new Field_short(ptr,field_length,null_pos,null_bit, unireg_check, field_name, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); - case FIELD_TYPE_INT24: + case MYSQL_TYPE_INT24: return new Field_medium(ptr,field_length,null_pos,null_bit, unireg_check, field_name, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); - case FIELD_TYPE_LONG: + case MYSQL_TYPE_LONG: return new Field_long(ptr,field_length,null_pos,null_bit, unireg_check, field_name, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); - case FIELD_TYPE_LONGLONG: + case MYSQL_TYPE_LONGLONG: return new Field_longlong(ptr,field_length,null_pos,null_bit, unireg_check, field_name, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); - case FIELD_TYPE_TIMESTAMP: + case MYSQL_TYPE_TIMESTAMP: return new Field_timestamp(ptr,field_length, null_pos, null_bit, unireg_check, field_name, share, field_charset); - case FIELD_TYPE_YEAR: + case MYSQL_TYPE_YEAR: return new Field_year(ptr,field_length,null_pos,null_bit, unireg_check, field_name); - case FIELD_TYPE_DATE: + case MYSQL_TYPE_DATE: return new Field_date(ptr,null_pos,null_bit, unireg_check, field_name, field_charset); - case FIELD_TYPE_NEWDATE: + case MYSQL_TYPE_NEWDATE: return new Field_newdate(ptr,null_pos,null_bit, unireg_check, field_name, field_charset); - case FIELD_TYPE_TIME: + case MYSQL_TYPE_TIME: return new Field_time(ptr,null_pos,null_bit, unireg_check, field_name, field_charset); - case FIELD_TYPE_DATETIME: + case MYSQL_TYPE_DATETIME: return new Field_datetime(ptr,null_pos,null_bit, unireg_check, field_name, field_charset); - case FIELD_TYPE_NULL: + case MYSQL_TYPE_NULL: return new Field_null(ptr, field_length, unireg_check, field_name, field_charset); - case FIELD_TYPE_BIT: + case MYSQL_TYPE_BIT: return f_bit_as_char(pack_flag) ? new Field_bit_as_char(ptr, field_length, null_pos, null_bit, unireg_check, field_name) : @@ -9224,12 +9303,12 @@ create_field::create_field(Field *old_field,Field *orig_field) portable_sizeof_char_ptr); switch (sql_type) { - case FIELD_TYPE_BLOB: + case MYSQL_TYPE_BLOB: switch (pack_length - portable_sizeof_char_ptr) { - case 1: sql_type= FIELD_TYPE_TINY_BLOB; break; - case 2: sql_type= FIELD_TYPE_BLOB; break; - case 3: sql_type= FIELD_TYPE_MEDIUM_BLOB; break; - default: sql_type= FIELD_TYPE_LONG_BLOB; break; + case 1: sql_type= MYSQL_TYPE_TINY_BLOB; break; + case 2: sql_type= MYSQL_TYPE_BLOB; break; + case 3: sql_type= MYSQL_TYPE_MEDIUM_BLOB; break; + default: sql_type= MYSQL_TYPE_LONG_BLOB; break; } length/= charset->mbmaxlen; key_length/= charset->mbmaxlen; @@ -9248,7 +9327,7 @@ create_field::create_field(Field *old_field,Field *orig_field) length= (length+charset->mbmaxlen-1) / charset->mbmaxlen; break; #ifdef HAVE_SPATIAL - case FIELD_TYPE_GEOMETRY: + case MYSQL_TYPE_GEOMETRY: geom_type= ((Field_geom*)old_field)->geom_type; break; #endif @@ -9265,7 +9344,7 @@ create_field::create_field(Field *old_field,Field *orig_field) if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) && old_field->ptr && orig_field && - (sql_type != FIELD_TYPE_TIMESTAMP || /* set def only if */ + (sql_type != MYSQL_TYPE_TIMESTAMP || /* set def only if */ old_field->table->timestamp_field != old_field || /* timestamp field */ unireg_check == Field::TIMESTAMP_UN_FIELD)) /* has default val */ { @@ -9294,12 +9373,13 @@ create_field::create_field(Field *old_field,Field *orig_field) maximum possible display length for blob SYNOPSIS - Field_blob::max_length() + Field_blob::max_display_length() RETURN length */ -uint32 Field_blob::max_length() + +uint32 Field_blob::max_display_length() { switch (packlength) { diff --git a/sql/field.h b/sql/field.h index 9b81931d416..f27ed8b9394 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -31,7 +30,7 @@ class Send_field; class Protocol; class create_field; struct st_cache_field; -void field_conv(Field *to,Field *from); +int field_conv(Field *to,Field *from); inline uint get_enum_pack_length(int elements) { @@ -153,18 +152,18 @@ public: /* data_length() return the "real size" of the data in memory. */ - virtual uint32 data_length(const char *from) { return pack_length(); } + virtual uint32 data_length() { return pack_length(); } virtual uint32 sort_length() const { return pack_length(); } - virtual void reset(void) { bzero(ptr,pack_length()); } + virtual int reset(void) { bzero(ptr,pack_length()); return 0; } virtual void reset_fields() {} virtual void set_default() { - my_ptrdiff_t offset = (my_ptrdiff_t) (table->s->default_values - + my_ptrdiff_t l_offset= (my_ptrdiff_t) (table->s->default_values - table->record[0]); - memcpy(ptr, ptr + offset, pack_length()); + memcpy(ptr, ptr + l_offset, pack_length()); if (null_ptr) *null_ptr= ((*null_ptr & (uchar) ~null_bit) | - null_ptr[offset] & null_bit); + null_ptr[l_offset] & null_bit); } virtual bool binary() const { return 1; } virtual bool zero_pack() const { return 1; } @@ -239,7 +238,7 @@ public: */ my_size_t last_null_byte() const { my_size_t bytes= do_last_null_byte(); - DBUG_PRINT("debug", ("last_null_byte() ==> %d", bytes)); + DBUG_PRINT("debug", ("last_null_byte() ==> %ld", (long) bytes)); DBUG_ASSERT(bytes <= table->s->null_bytes); return bytes; } @@ -277,7 +276,7 @@ public: { memcpy(buff,ptr,length); } inline void set_image(char *buff,uint length, CHARSET_INFO *cs) { memcpy(ptr,buff,length); } - virtual void get_key_image(char *buff, uint length, imagetype type) + virtual void get_key_image(char *buff, uint length, imagetype type_arg) { get_image(buff,length, &my_charset_bin); } virtual void set_key_image(char *buff,uint length) { set_image(buff,length, &my_charset_bin); } @@ -342,7 +341,10 @@ public: virtual int pack_cmp(const char *b, uint key_length_arg, my_bool insert_or_update) { return cmp(ptr,b); } - uint offset(); // Should be inline ... + uint offset(byte *record) + { + return (uint) (ptr - (char*) record); + } void copy_from_tmp(int offset); uint fill_cache_field(struct st_cache_field *copy); virtual bool get_date(TIME *ltime,uint fuzzydate); @@ -350,7 +352,10 @@ public: virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; } virtual CHARSET_INFO *sort_charset(void) const { return charset(); } virtual bool has_charset(void) const { return FALSE; } - virtual void set_charset(CHARSET_INFO *charset) { } + virtual void set_charset(CHARSET_INFO *charset_arg) { } + virtual enum Derivation derivation(void) const + { return DERIVATION_IMPLICIT; } + virtual void set_derivation(enum Derivation derivation_arg) { } bool set_warning(MYSQL_ERROR::enum_warning_level, unsigned int code, int cuted_increment); bool check_int(const char *str, int length, const char *int_end, @@ -375,7 +380,7 @@ public: } /* maximum possible display length */ - virtual uint32 max_length()= 0; + virtual uint32 max_display_length()= 0; virtual uint is_equal(create_field *new_field); /* convert decimal to longlong with overflow check */ @@ -446,6 +451,7 @@ public: class Field_str :public Field { protected: CHARSET_INFO *field_charset; + enum Derivation field_derivation; public: Field_str(char *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, @@ -458,9 +464,12 @@ public: int store(const char *to,uint length,CHARSET_INFO *cs)=0; uint size_of() const { return sizeof(*this); } CHARSET_INFO *charset(void) const { return field_charset; } - void set_charset(CHARSET_INFO *charset) { field_charset=charset; } + void set_charset(CHARSET_INFO *charset_arg) { field_charset= charset_arg; } + enum Derivation derivation(void) const { return field_derivation; } + virtual void set_derivation(enum Derivation derivation_arg) + { field_derivation= derivation_arg; } bool binary() const { return field_charset == &my_charset_bin; } - uint32 max_length() { return field_length; } + uint32 max_display_length() { return field_length; } friend class create_field; my_decimal *val_decimal(my_decimal *); virtual bool str_needs_quotes() { return TRUE; } @@ -475,9 +484,9 @@ class Field_longstr :public Field_str public: Field_longstr(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, - const char *field_name_arg, CHARSET_INFO *charset) + const char *field_name_arg, CHARSET_INFO *charset_arg) :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, - field_name_arg, charset) + field_name_arg, charset_arg) {} int store_decimal(const my_decimal *d); @@ -508,13 +517,13 @@ public: unireg_check_arg, field_name_arg, dec_arg, zero_arg, unsigned_arg) {} - enum_field_types type() const { return FIELD_TYPE_DECIMAL;} + enum_field_types type() const { return MYSQL_TYPE_DECIMAL;} enum ha_base_keytype key_type() const { return zerofill ? HA_KEYTYPE_BINARY : HA_KEYTYPE_NUM; } - void reset(void); - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); + int reset(void); + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -523,7 +532,7 @@ public: void overflow(bool negative); bool zero_pack() const { return 0; } void sql_type(String &str) const; - uint32 max_length() { return field_length; } + uint32 max_display_length() { return field_length; } }; @@ -546,15 +555,16 @@ public: Field_new_decimal(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, uint8 dec_arg, bool unsigned_arg); - enum_field_types type() const { return FIELD_TYPE_NEWDECIMAL;} + enum_field_types type() const { return MYSQL_TYPE_NEWDECIMAL;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } Item_result result_type () const { return DECIMAL_RESULT; } - void reset(void); + int reset(void); bool store_value(const my_decimal *decimal_value); void set_value_on_overflow(my_decimal *decimal_value, bool sign); int store(const char *to, uint length, CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); + int store_time(TIME *ltime, timestamp_type t_type); int store_decimal(const my_decimal *); double val_real(void); longlong val_int(void); @@ -564,7 +574,7 @@ public: void sort_string(char *buff, uint length); bool zero_pack() const { return 0; } void sql_type(String &str) const; - uint32 max_length() { return field_length; } + uint32 max_display_length() { return field_length; } uint size_of() const { return sizeof(*this); } uint32 pack_length() const { return (uint32) bin_size; } uint is_equal(create_field *new_field); @@ -582,13 +592,13 @@ public: 0, zero_arg,unsigned_arg) {} enum Item_result result_type () const { return INT_RESULT; } - enum_field_types type() const { return FIELD_TYPE_TINY;} + enum_field_types type() const { return MYSQL_TYPE_TINY;} enum ha_base_keytype key_type() const { return unsigned_flag ? HA_KEYTYPE_BINARY : HA_KEYTYPE_INT8; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - void reset(void) { ptr[0]=0; } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int reset(void) { ptr[0]=0; return 0; } double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -597,7 +607,7 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return 1; } void sql_type(String &str) const; - uint32 max_length() { return 4; } + uint32 max_display_length() { return 4; } }; @@ -617,13 +627,13 @@ public: NONE, field_name_arg, 0, 0, unsigned_arg) {} enum Item_result result_type () const { return INT_RESULT; } - enum_field_types type() const { return FIELD_TYPE_SHORT;} + enum_field_types type() const { return MYSQL_TYPE_SHORT;} enum ha_base_keytype key_type() const { return unsigned_flag ? HA_KEYTYPE_USHORT_INT : HA_KEYTYPE_SHORT_INT;} - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - void reset(void) { ptr[0]=ptr[1]=0; } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int reset(void) { ptr[0]=ptr[1]=0; return 0; } double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -632,7 +642,7 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return 2; } void sql_type(String &str) const; - uint32 max_length() { return 6; } + uint32 max_display_length() { return 6; } }; @@ -647,13 +657,13 @@ public: 0, zero_arg,unsigned_arg) {} enum Item_result result_type () const { return INT_RESULT; } - enum_field_types type() const { return FIELD_TYPE_INT24;} + enum_field_types type() const { return MYSQL_TYPE_INT24;} enum ha_base_keytype key_type() const { return unsigned_flag ? HA_KEYTYPE_UINT24 : HA_KEYTYPE_INT24; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); int store(longlong nr, bool unsigned_val); - void reset(void) { ptr[0]=ptr[1]=ptr[2]=0; } + int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; } double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -662,7 +672,7 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return 3; } void sql_type(String &str) const; - uint32 max_length() { return 8; } + uint32 max_display_length() { return 8; } }; @@ -682,13 +692,13 @@ public: NONE, field_name_arg,0,0,unsigned_arg) {} enum Item_result result_type () const { return INT_RESULT; } - enum_field_types type() const { return FIELD_TYPE_LONG;} + enum_field_types type() const { return MYSQL_TYPE_LONG;} enum ha_base_keytype key_type() const { return unsigned_flag ? HA_KEYTYPE_ULONG_INT : HA_KEYTYPE_LONG_INT; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - void reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; } double val_real(void); longlong val_int(void); bool send_binary(Protocol *protocol); @@ -697,7 +707,7 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return 4; } void sql_type(String &str) const; - uint32 max_length() { return 11; } + uint32 max_display_length() { return 11; } }; @@ -719,13 +729,17 @@ public: NONE, field_name_arg,0,0,unsigned_arg) {} enum Item_result result_type () const { return INT_RESULT; } - enum_field_types type() const { return FIELD_TYPE_LONGLONG;} + enum_field_types type() const { return MYSQL_TYPE_LONGLONG;} enum ha_base_keytype key_type() const { return unsigned_flag ? HA_KEYTYPE_ULONGLONG : HA_KEYTYPE_LONGLONG; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - void reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=ptr[4]=ptr[5]=ptr[6]=ptr[7]=0; } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int reset(void) + { + ptr[0]=ptr[1]=ptr[2]=ptr[3]=ptr[4]=ptr[5]=ptr[6]=ptr[7]=0; + return 0; + } double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -735,7 +749,7 @@ public: uint32 pack_length() const { return 8; } void sql_type(String &str) const; bool can_be_compared_as_longlong() const { return TRUE; } - uint32 max_length() { return 20; } + uint32 max_display_length() { return 20; } }; #endif @@ -755,12 +769,12 @@ public: :Field_real((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0, NONE, field_name_arg, dec_arg, 0, 0) {} - enum_field_types type() const { return FIELD_TYPE_FLOAT;} + enum_field_types type() const { return MYSQL_TYPE_FLOAT;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_FLOAT; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - void reset(void) { bzero(ptr,sizeof(float)); } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int reset(void) { bzero(ptr,sizeof(float)); return 0; } double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -769,31 +783,40 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return sizeof(float); } void sql_type(String &str) const; - uint32 max_length() { return 24; } + uint32 max_display_length() { return 24; } }; class Field_double :public Field_real { public: + my_bool not_fixed; Field_double(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, uint8 dec_arg,bool zero_arg,bool unsigned_arg) :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, - dec_arg, zero_arg, unsigned_arg) + dec_arg, zero_arg, unsigned_arg), + not_fixed(dec_arg >= NOT_FIXED_DEC) {} Field_double(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, uint8 dec_arg) - :Field_real((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0, - NONE, field_name_arg, dec_arg, 0, 0) + :Field_real((char*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0, + NONE, field_name_arg, dec_arg, 0, 0), + not_fixed(dec_arg >= NOT_FIXED_DEC) {} - enum_field_types type() const { return FIELD_TYPE_DOUBLE;} + Field_double(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, + uint8 dec_arg, my_bool not_fixed_srg) + :Field_real((char*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0, + NONE, field_name_arg, dec_arg, 0, 0), + not_fixed(not_fixed_srg) + {} + enum_field_types type() const { return MYSQL_TYPE_DOUBLE;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_DOUBLE; } int store(const char *to,uint length,CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); - void reset(void) { bzero(ptr,sizeof(double)); } + int reset(void) { bzero(ptr,sizeof(double)); return 0; } double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -802,7 +825,8 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return sizeof(double); } void sql_type(String &str) const; - uint32 max_length() { return 53; } + uint32 max_display_length() { return 53; } + uint size_of() const { return sizeof(*this); } }; @@ -817,13 +841,13 @@ public: :Field_str(ptr_arg, len_arg, null, 1, unireg_check_arg, field_name_arg, cs) {} - enum_field_types type() const { return FIELD_TYPE_NULL;} + enum_field_types type() const { return MYSQL_TYPE_NULL;} int store(const char *to, uint length, CHARSET_INFO *cs) { null[0]=1; return 0; } - int store(double nr) { null[0]=1; return 0; } - int store(longlong nr, bool unsigned_val) { null[0]=1; return 0; } - int store_decimal(const my_decimal *d) { null[0]=1; return 0; } - void reset(void) {} + int store(double nr) { null[0]=1; return 0; } + int store(longlong nr, bool unsigned_val) { null[0]=1; return 0; } + int store_decimal(const my_decimal *d) { null[0]=1; return 0; } + int reset(void) { return 0; } double val_real(void) { return 0.0;} longlong val_int(void) { return 0;} my_decimal *val_decimal(my_decimal *) { return 0; } @@ -834,7 +858,7 @@ public: uint32 pack_length() const { return 0; } void sql_type(String &str) const; uint size_of() const { return sizeof(*this); } - uint32 max_length() { return 4; } + uint32 max_display_length() { return 4; } }; @@ -846,13 +870,13 @@ public: TABLE_SHARE *share, CHARSET_INFO *cs); Field_timestamp(bool maybe_null_arg, const char *field_name_arg, CHARSET_INFO *cs); - enum_field_types type() const { return FIELD_TYPE_TIMESTAMP;} + enum_field_types type() const { return MYSQL_TYPE_TIMESTAMP;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } enum Item_result cmp_type () const { return INT_RESULT; } int store(const char *to,uint length,CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); - void reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; } + int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; } double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -899,7 +923,7 @@ public: :Field_tiny(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, 1, 1) {} - enum_field_types type() const { return FIELD_TYPE_YEAR;} + enum_field_types type() const { return MYSQL_TYPE_YEAR;} int store(const char *to,uint length,CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); @@ -924,13 +948,13 @@ public: CHARSET_INFO *cs) :Field_str((char*) 0,10, maybe_null_arg ? (uchar*) "": 0,0, NONE, field_name_arg, cs) {} - enum_field_types type() const { return FIELD_TYPE_DATE;} + enum_field_types type() const { return MYSQL_TYPE_DATE;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } enum Item_result cmp_type () const { return INT_RESULT; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - void reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; } double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -952,15 +976,15 @@ public: :Field_str(ptr_arg, 10, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, cs) {} - enum_field_types type() const { return FIELD_TYPE_DATE;} - enum_field_types real_type() const { return FIELD_TYPE_NEWDATE; } + enum_field_types type() const { return MYSQL_TYPE_DATE;} + enum_field_types real_type() const { return MYSQL_TYPE_NEWDATE; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_UINT24; } enum Item_result cmp_type () const { return INT_RESULT; } int store(const char *to,uint length,CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); int store_time(TIME *ltime, timestamp_type type); - void reset(void) { ptr[0]=ptr[1]=ptr[2]=0; } + int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; } double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -988,14 +1012,14 @@ public: CHARSET_INFO *cs) :Field_str((char*) 0,8, maybe_null_arg ? (uchar*) "": 0,0, NONE, field_name_arg, cs) {} - enum_field_types type() const { return FIELD_TYPE_TIME;} + enum_field_types type() const { return MYSQL_TYPE_TIME;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_INT24; } enum Item_result cmp_type () const { return INT_RESULT; } int store_time(TIME *ltime, timestamp_type type); - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - void reset(void) { ptr[0]=ptr[1]=ptr[2]=0; } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; } double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -1023,7 +1047,7 @@ public: CHARSET_INFO *cs) :Field_str((char*) 0,19, maybe_null_arg ? (uchar*) "": 0,0, NONE, field_name_arg, cs) {} - enum_field_types type() const { return FIELD_TYPE_DATETIME;} + enum_field_types type() const { return MYSQL_TYPE_DATETIME;} #ifdef HAVE_LONG_LONG enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; } #endif @@ -1033,7 +1057,11 @@ public: int store(double nr); int store(longlong nr, bool unsigned_val); int store_time(TIME *ltime, timestamp_type type); - void reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=ptr[4]=ptr[5]=ptr[6]=ptr[7]=0; } + int reset(void) + { + ptr[0]=ptr[1]=ptr[2]=ptr[3]=ptr[4]=ptr[5]=ptr[6]=ptr[7]=0; + return 0; + } double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -1076,9 +1104,14 @@ public: enum ha_base_keytype key_type() const { return binary() ? HA_KEYTYPE_BINARY : HA_KEYTYPE_TEXT; } bool zero_pack() const { return 0; } - void reset(void) { charset()->cset->fill(charset(),ptr,field_length,' '); } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(longlong nr, bool unsigned_val); + int reset(void) + { + charset()->cset->fill(charset(),ptr,field_length, + (has_charset() ? ' ' : 0)); + return 0; + } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(longlong nr, bool unsigned_val); int store(double nr) { return Field_str::store(nr); } /* QQ: To be deleted */ double val_real(void); longlong val_int(void); @@ -1095,7 +1128,7 @@ public: uint packed_col_length(const char *to, uint length); uint max_packed_col_length(uint max_length); uint size_of() const { return sizeof(*this); } - enum_field_types real_type() const { return FIELD_TYPE_STRING; } + enum_field_types real_type() const { return MYSQL_TYPE_STRING; } bool has_charset(void) const { return charset() == &my_charset_bin ? FALSE : TRUE; } Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type); @@ -1130,7 +1163,7 @@ public: enum_field_types type() const { return MYSQL_TYPE_VARCHAR; } enum ha_base_keytype key_type() const; bool zero_pack() const { return 0; } - void reset(void) { bzero(ptr,field_length+length_bytes); } + int reset(void) { bzero(ptr,field_length+length_bytes); return 0; } uint32 pack_length() const { return (uint32) field_length+length_bytes; } uint32 key_length() const { return (uint32) field_length; } uint32 sort_length() const @@ -1167,7 +1200,7 @@ public: int key_cmp(const byte *str, uint length); uint packed_col_length(const char *to, uint length); uint max_packed_col_length(uint max_length); - uint32 data_length(const char *from); + uint32 data_length(); uint size_of() const { return sizeof(*this); } enum_field_types real_type() const { return MYSQL_TYPE_VARCHAR; } bool has_charset(void) const @@ -1206,13 +1239,13 @@ public: packlength= 4; if (set_packlength) { - uint32 char_length= len_arg/cs->mbmaxlen; - packlength= char_length <= 255 ? 1 : - char_length <= 65535 ? 2 : - char_length <= 16777215 ? 3 : 4; + uint32 l_char_length= len_arg/cs->mbmaxlen; + packlength= l_char_length <= 255 ? 1 : + l_char_length <= 65535 ? 2 : + l_char_length <= 16777215 ? 3 : 4; } } - enum_field_types type() const { return FIELD_TYPE_BLOB;} + enum_field_types type() const { return MYSQL_TYPE_BLOB;} enum ha_base_keytype key_type() const { return binary() ? HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2; } int store(const char *to,uint length,CHARSET_INFO *charset); @@ -1238,7 +1271,7 @@ public: { return (uint32) (((ulonglong) 1 << (packlength*8)) -1); } - void reset(void) { bzero(ptr, packlength+sizeof(char*)); } + int reset(void) { bzero(ptr, packlength+sizeof(char*)); return 0; } void reset_fields() { bzero((char*) &value,sizeof(value)); } void store_length(uint32 number); inline uint32 get_length(uint row_offset=0) @@ -1289,11 +1322,11 @@ public: uint max_packed_col_length(uint max_length); void free() { value.free(); } inline void clear_temporary() { bzero((char*) &value,sizeof(value)); } - friend void field_conv(Field *to,Field *from); + friend int field_conv(Field *to,Field *from); uint size_of() const { return sizeof(*this); } bool has_charset(void) const { return charset() == &my_charset_bin ? FALSE : TRUE; } - uint32 max_length(); + uint32 max_display_length(); }; @@ -1314,7 +1347,7 @@ public: :Field_blob(len_arg, maybe_null_arg, field_name_arg, &my_charset_bin) { geom_type= geom_type_arg; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_VARBINARY2; } - enum_field_types type() const { return FIELD_TYPE_GEOMETRY; } + enum_field_types type() const { return MYSQL_TYPE_GEOMETRY; } void sql_type(String &str) const; int store(const char *to, uint length, CHARSET_INFO *charset); int store(double nr); @@ -1322,6 +1355,7 @@ public: int store_decimal(const my_decimal *); void get_key_image(char *buff,uint length,imagetype type); uint size_of() const { return sizeof(*this); } + int reset(void) { return !maybe_null(); } }; #endif /*HAVE_SPATIAL*/ @@ -1343,14 +1377,14 @@ public: { flags|=ENUM_FLAG; } - enum_field_types type() const { return FIELD_TYPE_STRING; } + Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type); + enum_field_types type() const { return MYSQL_TYPE_STRING; } enum Item_result cmp_type () const { return INT_RESULT; } enum Item_result cast_to_int_type () const { return INT_RESULT; } enum ha_base_keytype key_type() const; int store(const char *to,uint length,CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); - void reset() { bzero(ptr,packlength); } double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -1360,7 +1394,7 @@ public: void store_type(ulonglong value); void sql_type(String &str) const; uint size_of() const { return sizeof(*this); } - enum_field_types real_type() const { return FIELD_TYPE_ENUM; } + enum_field_types real_type() const { return MYSQL_TYPE_ENUM; } virtual bool zero_pack() const { return 0; } bool optimize_range(uint idx, uint part) { return 0; } bool eq_def(Field *field); @@ -1390,7 +1424,7 @@ public: virtual bool zero_pack() const { return 1; } String *val_str(String*,String *); void sql_type(String &str) const; - enum_field_types real_type() const { return FIELD_TYPE_SET; } + enum_field_types real_type() const { return MYSQL_TYPE_SET; } bool has_charset(void) const { return TRUE; } }; @@ -1418,13 +1452,13 @@ public: Field_bit(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg, enum utype unireg_check_arg, const char *field_name_arg); - enum_field_types type() const { return FIELD_TYPE_BIT; } + enum_field_types type() const { return MYSQL_TYPE_BIT; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_BIT; } uint32 key_length() const { return (uint32) (field_length + 7) / 8; } - uint32 max_length() { return field_length; } + uint32 max_display_length() { return field_length; } uint size_of() const { return sizeof(*this); } Item_result result_type () const { return INT_RESULT; } - void reset(void) { bzero(ptr, bytes_in_rec); } + int reset(void) { bzero(ptr, bytes_in_rec); return 0; } int store(const char *to, uint length, CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); @@ -1481,6 +1515,13 @@ private: }; +/** + BIT field represented as chars for non-MyISAM tables. + + @todo The inheritance relationship is backwards since Field_bit is + an extended version of Field_bit_as_char and not the other way + around. Hence, we should refactor it to fix the hierarchy order. + */ class Field_bit_as_char: public Field_bit { public: Field_bit_as_char(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, diff --git a/sql/field_conv.cc b/sql/field_conv.cc index 7bc6c432d1c..805aba01754 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -119,12 +118,12 @@ set_field_to_null(Field *field) return 0; } field->reset(); - if (current_thd->count_cuted_fields == CHECK_FIELD_WARN) + if (field->table->in_use->count_cuted_fields == CHECK_FIELD_WARN) { field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); return 0; } - if (!current_thd->no_errors) + if (!field->table->in_use->no_errors) my_error(ER_BAD_NULL_ERROR, MYF(0), field->field_name); return -1; } @@ -165,7 +164,7 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions) when set to NULL (TIMESTAMP fields which allow setting to NULL are handled by first check). */ - if (field->type() == FIELD_TYPE_TIMESTAMP) + if (field->type() == MYSQL_TYPE_TIMESTAMP) { ((Field_timestamp*) field)->set_time(); return 0; // Ok to set time to NULL @@ -176,12 +175,12 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions) field->table->auto_increment_field_not_null= FALSE; return 0; // field is set in handler.cc } - if (current_thd->count_cuted_fields == CHECK_FIELD_WARN) + if (field->table->in_use->count_cuted_fields == CHECK_FIELD_WARN) { field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_BAD_NULL_ERROR, 1); return 0; } - if (!current_thd->no_errors) + if (!field->table->in_use->no_errors) my_error(ER_BAD_NULL_ERROR, MYF(0), field->field_name); return -1; } @@ -308,6 +307,21 @@ static void do_field_string(Copy_field *copy) } +static void do_field_varbinary_pre50(Copy_field *copy) +{ + char buff[MAX_FIELD_WIDTH]; + copy->tmp.set_quick(buff,sizeof(buff),copy->tmp.charset()); + copy->from_field->val_str(©->tmp); + + /* Use the same function as in 4.1 to trim trailing spaces */ + uint length= my_lengthsp_8bit(&my_charset_bin, copy->tmp.c_ptr_quick(), + copy->from_field->field_length); + + copy->to_field->store(copy->tmp.c_ptr_quick(), length, + copy->tmp.charset()); +} + + static void do_field_int(Copy_field *copy) { longlong value= copy->from_field->val_int(); @@ -403,7 +417,7 @@ static void do_varstring1(Copy_field *copy) if (length > copy->to_length- 1) { length=copy->to_length - 1; - if (current_thd->count_cuted_fields) + if (copy->from_field->table->in_use->count_cuted_fields) copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } @@ -418,7 +432,7 @@ static void do_varstring2(Copy_field *copy) if (length > copy->to_length- HA_KEY_BLOB_LENGTH) { length=copy->to_length-HA_KEY_BLOB_LENGTH; - if (current_thd->count_cuted_fields) + if (copy->from_field->table->in_use->count_cuted_fields) copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } @@ -485,7 +499,7 @@ void Copy_field::set(char *to,Field *from) void Copy_field::set(Field *to,Field *from,bool save) { - if (to->type() == FIELD_TYPE_NULL) + if (to->type() == MYSQL_TYPE_NULL) { to_null_ptr=0; // For easy debugging to_ptr=0; @@ -519,7 +533,7 @@ void Copy_field::set(Field *to,Field *from,bool save) } else { - if (to_field->type() == FIELD_TYPE_TIMESTAMP) + if (to_field->type() == MYSQL_TYPE_TIMESTAMP) do_copy= do_copy_timestamp; // Automatic timestamp else if (to_field == to_field->table->next_number_field) do_copy= do_copy_next_number; @@ -563,13 +577,22 @@ void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*) } else { - if (to->real_type() == FIELD_TYPE_BIT || - from->real_type() == FIELD_TYPE_BIT) + if (to->real_type() == MYSQL_TYPE_BIT || + from->real_type() == MYSQL_TYPE_BIT) return do_field_int; // Check if identical fields if (from->result_type() == STRING_RESULT) { /* + Detect copy from pre 5.0 varbinary to varbinary as of 5.0 and + use special copy function that removes trailing spaces and thus + repairs data. + */ + if (from->type() == MYSQL_TYPE_VAR_STRING && !from->has_charset() && + to->type() == MYSQL_TYPE_VARCHAR && !to->has_charset()) + return do_field_varbinary_pre50; + + /* If we are copying date or datetime's we have to check the dates if we don't allow 'all' dates. */ @@ -577,17 +600,17 @@ void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*) !compatible_db_low_byte_first || ((to->table->in_use->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES)) && - to->type() == FIELD_TYPE_DATE || - to->type() == FIELD_TYPE_DATETIME)) + to->type() == MYSQL_TYPE_DATE || + to->type() == MYSQL_TYPE_DATETIME)) { - if (from->real_type() == FIELD_TYPE_ENUM || - from->real_type() == FIELD_TYPE_SET) + if (from->real_type() == MYSQL_TYPE_ENUM || + from->real_type() == MYSQL_TYPE_SET) if (to->result_type() != STRING_RESULT) return do_field_int; // Convert SET to number return do_field_string; } - if (to->real_type() == FIELD_TYPE_ENUM || - to->real_type() == FIELD_TYPE_SET) + if (to->real_type() == MYSQL_TYPE_ENUM || + to->real_type() == MYSQL_TYPE_SET) { if (!to->eq_def(from)) return do_field_string; @@ -620,7 +643,7 @@ void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*) to_length != from_length || !compatible_db_low_byte_first) { - if (to->real_type() == FIELD_TYPE_DECIMAL || + if (to->real_type() == MYSQL_TYPE_DECIMAL || to->result_type() == STRING_RESULT) return do_field_string; if (to->result_type() == INT_RESULT) @@ -631,7 +654,7 @@ void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*) { if (!to->eq_def(from) || !compatible_db_low_byte_first) { - if (to->real_type() == FIELD_TYPE_DECIMAL) + if (to->real_type() == MYSQL_TYPE_DECIMAL) return do_field_string; if (to->result_type() == INT_RESULT) return do_field_int; @@ -655,25 +678,25 @@ void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*) /* Simple quick field convert that is called on insert */ -void field_conv(Field *to,Field *from) +int field_conv(Field *to,Field *from) { if (to->real_type() == from->real_type() && - !(to->type() == FIELD_TYPE_BLOB && to->table->copy_blobs)) + !(to->type() == MYSQL_TYPE_BLOB && to->table->copy_blobs)) { if (to->pack_length() == from->pack_length() && !(to->flags & UNSIGNED_FLAG && !(from->flags & UNSIGNED_FLAG)) && - to->real_type() != FIELD_TYPE_ENUM && - to->real_type() != FIELD_TYPE_SET && - to->real_type() != FIELD_TYPE_BIT && - (to->real_type() != FIELD_TYPE_NEWDECIMAL || + to->real_type() != MYSQL_TYPE_ENUM && + to->real_type() != MYSQL_TYPE_SET && + to->real_type() != MYSQL_TYPE_BIT && + (to->real_type() != MYSQL_TYPE_NEWDECIMAL || (to->field_length == from->field_length && (((Field_num*)to)->dec == ((Field_num*)from)->dec))) && from->charset() == to->charset() && to->table->s->db_low_byte_first == from->table->s->db_low_byte_first && (!(to->table->in_use->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES)) || - to->type() != FIELD_TYPE_DATE && - to->type() != FIELD_TYPE_DATETIME) && + to->type() != MYSQL_TYPE_DATE && + to->type() != MYSQL_TYPE_DATETIME) && (from->real_type() != MYSQL_TYPE_VARCHAR || ((Field_varstring*)from)->length_bytes == ((Field_varstring*)to)->length_bytes)) @@ -683,10 +706,10 @@ void field_conv(Field *to,Field *from) if (to->ptr != from->ptr) #endif memcpy(to->ptr,from->ptr,to->pack_length()); - return; + return 0; } } - if (to->type() == FIELD_TYPE_BLOB) + if (to->type() == MYSQL_TYPE_BLOB) { // Be sure the value is stored Field_blob *blob=(Field_blob*) to; from->val_str(&blob->value); @@ -699,14 +722,13 @@ void field_conv(Field *to,Field *from) from->real_type() != MYSQL_TYPE_STRING && from->real_type() != MYSQL_TYPE_VARCHAR)) blob->value.copy(); - blob->store(blob->value.ptr(),blob->value.length(),from->charset()); - return; + return blob->store(blob->value.ptr(),blob->value.length(),from->charset()); } if ((from->result_type() == STRING_RESULT && (to->result_type() == STRING_RESULT || - (from->real_type() != FIELD_TYPE_ENUM && - from->real_type() != FIELD_TYPE_SET))) || - to->type() == FIELD_TYPE_DECIMAL) + (from->real_type() != MYSQL_TYPE_ENUM && + from->real_type() != MYSQL_TYPE_SET))) || + to->type() == MYSQL_TYPE_DECIMAL) { char buff[MAX_FIELD_WIDTH]; String result(buff,sizeof(buff),from->charset()); @@ -717,15 +739,15 @@ void field_conv(Field *to,Field *from) end with \0. Can be replaced with .ptr() when we have our own string->double conversion. */ - to->store(result.c_ptr_quick(),result.length(),from->charset()); + return to->store(result.c_ptr_quick(),result.length(),from->charset()); } else if (from->result_type() == REAL_RESULT) - to->store(from->val_real()); + return to->store(from->val_real()); else if (from->result_type() == DECIMAL_RESULT) { my_decimal buff; - to->store_decimal(from->val_decimal(&buff)); + return to->store_decimal(from->val_decimal(&buff)); } else - to->store(from->val_int(), test(from->flags & UNSIGNED_FLAG)); + return to->store(from->val_int(), test(from->flags & UNSIGNED_FLAG)); } diff --git a/sql/filesort.cc b/sql/filesort.cc index 01f3bb97557..2f9a96472ca 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -105,7 +104,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, uint maxbuffer; BUFFPEK *buffpek; ha_rows records= HA_POS_ERROR; - uchar **sort_keys; + uchar **sort_keys= 0; IO_CACHE tempfile, buffpek_pointers, *selected_records_file, *outfile; SORTPARAM param; bool multi_byte_charset; @@ -115,6 +114,8 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, DBUG_PUSH(""); /* No DBUG here */ #endif FILESORT_INFO table_sort; + TABLE_LIST *tab= table->pos_in_table_list; + Item_subselect *subselect= tab ? tab->containing_subselect() : 0; /* Don't use table->sort in filesort as it is also used by QUICK_INDEX_MERGE_SELECT. Work with a copy and put it back at the end @@ -127,7 +128,6 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, my_b_clear(&tempfile); my_b_clear(&buffpek_pointers); buffpek=0; - sort_keys= (uchar **) NULL; error= 1; bzero((char*) ¶m,sizeof(param)); param.sort_length= sortlength(thd, sortorder, s_length, &multi_byte_charset); @@ -208,13 +208,15 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, ulong old_memavl; ulong keys= memavl/(param.rec_length+sizeof(char*)); param.keys=(uint) min(records+1, keys); - if ((sort_keys= (uchar **) make_char_array(param.keys, param.rec_length, - MYF(0)))) + if (table_sort.sort_keys || + (table_sort.sort_keys= (uchar **) make_char_array(param.keys, param.rec_length, + MYF(0)))) break; old_memavl=memavl; if ((memavl=memavl/4*3) < min_sort_memory && old_memavl > min_sort_memory) memavl= min_sort_memory; } + sort_keys= table_sort.sort_keys; if (memavl < min_sort_memory) { my_error(ER_OUTOFMEMORY,MYF(ME_ERROR+ME_WAITTANG), @@ -241,8 +243,12 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, } else { - if (!(buffpek=read_buffpek_from_file(&buffpek_pointers, maxbuffer))) + if (!table_sort.buffpek && table_sort.buffpek_len < maxbuffer && + !(table_sort.buffpek= + (byte *) read_buffpek_from_file(&buffpek_pointers, maxbuffer))) goto err; + buffpek= (BUFFPEK *) table_sort.buffpek; + table_sort.buffpek_len= maxbuffer; close_cached_file(&buffpek_pointers); /* Open cached file if it isn't open */ if (! my_b_inited(outfile) && @@ -275,8 +281,14 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, err: if (param.tmp_buffer) x_free(param.tmp_buffer); - x_free((gptr) sort_keys); - x_free((gptr) buffpek); + if (!subselect || !subselect->is_uncacheable()) + { + x_free((gptr) sort_keys); + table_sort.sort_keys= 0; + x_free((gptr) buffpek); + table_sort.buffpek= 0; + table_sort.buffpek_len= 0; + } close_cached_file(&tempfile); close_cached_file(&buffpek_pointers); if (my_b_inited(outfile)) @@ -302,18 +314,32 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, DBUG_POP(); /* Ok to DBUG */ #endif memcpy(&table->sort, &table_sort, sizeof(FILESORT_INFO)); - DBUG_PRINT("exit",("records: %ld",records)); + DBUG_PRINT("exit",("records: %ld", (long) records)); DBUG_RETURN(error ? HA_POS_ERROR : records); } /* filesort */ -void filesort_free_buffers(TABLE *table) +void filesort_free_buffers(TABLE *table, bool full) { if (table->sort.record_pointers) { my_free((gptr) table->sort.record_pointers,MYF(0)); table->sort.record_pointers=0; } + if (full) + { + if (table->sort.sort_keys ) + { + x_free((gptr) table->sort.sort_keys); + table->sort.sort_keys= 0; + } + if (table->sort.buffpek) + { + x_free((gptr) table->sort.buffpek); + table->sort.buffpek= 0; + table->sort.buffpek_len= 0; + } + } if (table->sort.addon_buf) { my_free((char *) table->sort.addon_buf, MYF(0)); @@ -408,7 +434,8 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, byte *ref_pos,*next_pos,ref_buff[MAX_REFLENGTH]; my_off_t record; TABLE *sort_form; - volatile THD::killed_state *killed= ¤t_thd->killed; + THD *thd= current_thd; + volatile THD::killed_state *killed= &thd->killed; handler *file; MY_BITMAP *save_read_set, *save_write_set; DBUG_ENTER("find_all_keys"); @@ -521,6 +548,9 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, } else file->unlock_row(); + /* It does not make sense to read more keys in case of a fatal error */ + if (thd->net.report_error) + DBUG_RETURN(HA_POS_ERROR); } if (quick_select) { @@ -860,12 +890,14 @@ static void make_sortkey(register SORTPARAM *param, } else { - uchar *end= (uchar*) field->pack((char *) to, field->ptr); #ifdef HAVE_purify + uchar *end= (uchar*) field->pack((char *) to, field->ptr); uint length= (uint) ((to + addonf->length) - end); DBUG_ASSERT((int) length >= 0); if (length) bzero(end, length); +#else + (void) field->pack((char *) to, field->ptr); #endif } to+= addonf->length; @@ -1023,7 +1055,7 @@ uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek, Put all room used by freed buffer to use in adjacent buffer. Note, that we can't simply distribute memory evenly between all buffers, because new areas must not overlap with old ones. - SYNOPSYS + SYNOPSIS reuse_freed_buff() queue IN list of non-empty buffers, without freed buffer reuse IN empty buffer diff --git a/sql/frm_crypt.cc b/sql/frm_crypt.cc index 8dd70900648..590205e83ab 100644 --- a/sql/frm_crypt.cc +++ b/sql/frm_crypt.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/gen_lex_hash.cc b/sql/gen_lex_hash.cc index 5a8bd48d699..2d78999017a 100644 --- a/sql/gen_lex_hash.cc +++ b/sql/gen_lex_hash.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -206,9 +205,10 @@ void insert_symbols() void insert_sql_functions() { - size_t i= 0; + int i= 0; SYMBOL *cur; - for (cur= sql_functions; i<array_elements(sql_functions); cur++, i++){ + for (cur= sql_functions; i < (int) array_elements(sql_functions); cur++, i++) + { hash_lex_struct *root= get_hash_struct_by_len(&root_by_len,cur->length,&max_len); insert_into_hash(root,cur->name,0,-i-1,1); @@ -445,10 +445,24 @@ int main(int argc,char **argv) /* Broken up to indicate that it's not advice to you, gentle reader. */ printf("/*\n\n Do " "not " "edit " "this " "file " "directly!\n\n*/\n"); - printf("/* Copyright (C) 2001-2004 MySQL AB\n\ - This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n\ - and you are welcome to modify and redistribute it under the GPL license\n\ - \n*/\n\n"); + printf("\ +/* Copyright (C) 2001-2004 MySQL AB\n\ +\n\ + This program is free software; you can redistribute it and/or modify\n\ + it under the terms of the GNU General Public License as published by\n\ + the Free Software Foundation; version 2 of the License.\n\ +\n\ + This program is distributed in the hope that it will be useful,\n\ + but WITHOUT ANY WARRANTY; without even the implied warranty of\n\ + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\ + GNU General Public License for more details.\n\ +\n\ + You should have received a copy of the GNU General Public License\n\ + along with this program; see the file COPYING. If not, write to the\n\ + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston\n\ + MA 02110-1301 USA. */\n\ +\n\ +"); /* Broken up to indicate that it's not advice to you, gentle reader. */ printf("/* Do " "not " "edit " "this " "file! This is generated by " diff --git a/sql/gstream.cc b/sql/gstream.cc index 4083cb2fe71..46e12b6ef3b 100644 --- a/sql/gstream.cc +++ b/sql/gstream.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/gstream.h b/sql/gstream.h index bfbf28851ce..10274635413 100644 --- a/sql/gstream.h +++ b/sql/gstream.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2004 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 13ea471511c..872d97a6fa8 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -134,7 +133,7 @@ static uint ndbcluster_alter_table_flags(uint flags) } static int ndbcluster_inited= 0; -int ndbcluster_util_inited= 0; +static int ndbcluster_terminating= 0; static Ndb* g_ndb= NULL; Ndb_cluster_connection* g_ndb_cluster_connection= NULL; @@ -151,16 +150,16 @@ static byte *ndbcluster_get_key(NDB_SHARE *share,uint *length, #ifdef HAVE_NDB_BINLOG static int rename_share(NDB_SHARE *share, const char *new_key); #endif -static void ndb_set_fragmentation(NDBTAB &tab, TABLE *table, uint pk_len); - -static int ndb_get_table_statistics(Ndb*, const NDBTAB *, +static int ndb_get_table_statistics(ha_ndbcluster*, bool, Ndb*, const NDBTAB *, struct Ndb_statistics *); // Util thread variables pthread_t ndb_util_thread; +int ndb_util_thread_running= 0; pthread_mutex_t LOCK_ndb_util_thread; pthread_cond_t COND_ndb_util_thread; +pthread_cond_t COND_ndb_util_ready; pthread_handler_t ndb_util_thread_func(void *arg); ulong ndb_cache_check_time; @@ -260,16 +259,16 @@ static int ndb_to_mysql_error(const NdbError *ndberr) int execute_no_commit_ignore_no_key(ha_ndbcluster *h, NdbTransaction *trans) { - int res= trans->execute(NdbTransaction::NoCommit, - NdbTransaction::AO_IgnoreError, - h->m_force_send); - if (res == 0) - return 0; + if (trans->execute(NdbTransaction::NoCommit, + NdbOperation::AO_IgnoreError, + h->m_force_send) == -1) + return -1; const NdbError &err= trans->getNdbError(); - if (err.classification != NdbError::ConstraintViolation && + if (err.classification != NdbError::NoError && + err.classification != NdbError::ConstraintViolation && err.classification != NdbError::NoDataFound) - return res; + return -1; return 0; } @@ -287,7 +286,7 @@ int execute_no_commit(ha_ndbcluster *h, NdbTransaction *trans, return h->m_ignore_no_key ? execute_no_commit_ignore_no_key(h,trans) : trans->execute(NdbTransaction::NoCommit, - NdbTransaction::AbortOnError, + NdbOperation::AbortOnError, h->m_force_send); } @@ -300,7 +299,7 @@ int execute_commit(ha_ndbcluster *h, NdbTransaction *trans) return 0; #endif return trans->execute(NdbTransaction::Commit, - NdbTransaction::AbortOnError, + NdbOperation::AbortOnError, h->m_force_send); } @@ -313,7 +312,7 @@ int execute_commit(THD *thd, NdbTransaction *trans) return 0; #endif return trans->execute(NdbTransaction::Commit, - NdbTransaction::AbortOnError, + NdbOperation::AbortOnError, thd->variables.ndb_force_send); } @@ -328,7 +327,7 @@ int execute_no_commit_ie(ha_ndbcluster *h, NdbTransaction *trans, #endif h->release_completed_operations(trans, force_release); return trans->execute(NdbTransaction::NoCommit, - NdbTransaction::AO_IgnoreError, + NdbOperation::AO_IgnoreError, h->m_force_send); } @@ -414,7 +413,8 @@ Thd_ndb::get_open_table(THD *thd, const void *key) thd_ndb_share->stat.no_uncommitted_rows_count= 0; thd_ndb_share->stat.records= ~(ha_rows)0; } - DBUG_PRINT("exit", ("thd_ndb_share: 0x%x key: 0x%x", thd_ndb_share, key)); + DBUG_PRINT("exit", ("thd_ndb_share: 0x%lx key: 0x%lx", + (long) thd_ndb_share, (long) key)); DBUG_RETURN(thd_ndb_share); } @@ -442,61 +442,61 @@ ha_rows ha_ndbcluster::records() { ha_rows retval; DBUG_ENTER("ha_ndbcluster::records"); - struct Ndb_local_table_statistics *info= m_table_info; + struct Ndb_local_table_statistics *local_info= m_table_info; DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d", ((const NDBTAB *)m_table)->getTableId(), - info->no_uncommitted_rows_count)); + local_info->no_uncommitted_rows_count)); Ndb *ndb= get_ndb(); ndb->setDatabaseName(m_dbname); struct Ndb_statistics stat; - if (ndb_get_table_statistics(ndb, m_table, &stat) == 0) + if (ndb_get_table_statistics(this, TRUE, ndb, m_table, &stat) == 0) { retval= stat.row_count; } else { - /** - * Be consistent with BUG#19914 until we fix it properly - */ - DBUG_RETURN(-1); + DBUG_RETURN(HA_POS_ERROR); } THD *thd= current_thd; if (get_thd_ndb(thd)->error) - info->no_uncommitted_rows_count= 0; + local_info->no_uncommitted_rows_count= 0; - DBUG_RETURN(retval + info->no_uncommitted_rows_count); + DBUG_RETURN(retval + local_info->no_uncommitted_rows_count); } -void ha_ndbcluster::records_update() +int ha_ndbcluster::records_update() { if (m_ha_not_exact_count) - return; + return 0; DBUG_ENTER("ha_ndbcluster::records_update"); - struct Ndb_local_table_statistics *info= m_table_info; + int result= 0; + + struct Ndb_local_table_statistics *local_info= m_table_info; DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d", ((const NDBTAB *)m_table)->getTableId(), - info->no_uncommitted_rows_count)); - // if (info->records == ~(ha_rows)0) + local_info->no_uncommitted_rows_count)); { Ndb *ndb= get_ndb(); struct Ndb_statistics stat; ndb->setDatabaseName(m_dbname); - if (ndb_get_table_statistics(ndb, m_table, &stat) == 0) + result= ndb_get_table_statistics(this, TRUE, ndb, m_table, &stat); + if (result == 0) { stats.mean_rec_length= stat.row_size; stats.data_file_length= stat.fragment_memory; - info->records= stat.row_count; + local_info->records= stat.row_count; } } { THD *thd= current_thd; if (get_thd_ndb(thd)->error) - info->no_uncommitted_rows_count= 0; + local_info->no_uncommitted_rows_count= 0; } - stats.records= info->records+ info->no_uncommitted_rows_count; - DBUG_VOID_RETURN; + if (result == 0) + stats.records= local_info->records+ local_info->no_uncommitted_rows_count; + DBUG_RETURN(result); } void ha_ndbcluster::no_uncommitted_rows_execute_failure() @@ -513,11 +513,11 @@ void ha_ndbcluster::no_uncommitted_rows_update(int c) if (m_ha_not_exact_count) return; DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_update"); - struct Ndb_local_table_statistics *info= m_table_info; - info->no_uncommitted_rows_count+= c; + struct Ndb_local_table_statistics *local_info= m_table_info; + local_info->no_uncommitted_rows_count+= c; DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d", ((const NDBTAB *)m_table)->getTableId(), - info->no_uncommitted_rows_count)); + local_info->no_uncommitted_rows_count)); DBUG_VOID_RETURN; } @@ -561,7 +561,14 @@ int ha_ndbcluster::ndb_err(NdbTransaction *trans) if (res == HA_ERR_FOUND_DUPP_KEY) { if (m_rows_to_insert == 1) - m_dupkey= table_share->primary_key; + { + /* + We can only distinguish between primary and non-primary + violations here, so we need to return MAX_KEY for non-primary + to signal that key is unknown + */ + m_dupkey= err.code == 630 ? table_share->primary_key : MAX_KEY; + } else { /* We are batching inserts, offending key is not available */ @@ -754,8 +761,8 @@ int ha_ndbcluster::set_ndb_value(NdbOperation *ndb_op, Field *field, blob_ptr= (char*)""; } - DBUG_PRINT("value", ("set blob ptr=%p len=%u", - blob_ptr, blob_len)); + DBUG_PRINT("value", ("set blob ptr: 0x%lx len: %u", + (long) blob_ptr, blob_len)); DBUG_DUMP("value", (char*)blob_ptr, min(blob_len, 26)); if (set_blob_value) @@ -840,8 +847,8 @@ int get_ndb_blobs_value(TABLE* table, NdbValue* value_array, uint32 len= 0xffffffff; // Max uint32 if (ndb_blob->readData(buf, len) != 0) ERR_RETURN(ndb_blob->getNdbError()); - DBUG_PRINT("info", ("[%u] offset=%u buf=%p len=%u [ptrdiff=%d]", - i, offset, buf, len, (int)ptrdiff)); + DBUG_PRINT("info", ("[%u] offset: %u buf: 0x%lx len=%u [ptrdiff=%d]", + i, offset, (long) buf, len, (int)ptrdiff)); DBUG_ASSERT(len == len64); // Ugly hack assumes only ptr needs to be changed field_blob->ptr+= ptrdiff; @@ -947,7 +954,6 @@ int ha_ndbcluster::get_ndb_partition_id(NdbOperation *ndb_op) bool ha_ndbcluster::uses_blob_value() { - uint blob_fields; MY_BITMAP *bitmap; uint *blob_index, *blob_index_end; if (table_share->blob_fields == 0) @@ -958,8 +964,7 @@ bool ha_ndbcluster::uses_blob_value() blob_index_end= blob_index + table_share->blob_fields; do { - if (bitmap_is_set(table->write_set, - table->field[*blob_index]->field_index)) + if (bitmap_is_set(bitmap, table->field[*blob_index]->field_index)) return TRUE; } while (++blob_index != blob_index_end); return FALSE; @@ -1098,7 +1103,6 @@ int ha_ndbcluster::create_indexes(Ndb *ndb, TABLE *tab) const char *index_name; KEY* key_info= tab->key_info; const char **key_name= tab->s->keynames.type_names; - NDBDICT *dict= ndb->getDictionary(); DBUG_ENTER("ha_ndbcluster::create_indexes"); for (i= 0; i < tab->s->keys; i++, key_info++, key_name++) @@ -1164,8 +1168,8 @@ int ha_ndbcluster::add_index_handle(THD *thd, NDBDICT *dict, KEY *key_info, index= dict->getIndexGlobal(index_name, *m_table); if (!index) ERR_RETURN(dict->getNdbError()); - DBUG_PRINT("info", ("index: 0x%x id: %d version: %d.%d status: %d", - index, + DBUG_PRINT("info", ("index: 0x%lx id: %d version: %d.%d status: %d", + (long) index, index->getObjectId(), index->getObjectVersion() & 0xFFFFFF, index->getObjectVersion() >> 24, @@ -1208,8 +1212,8 @@ int ha_ndbcluster::add_index_handle(THD *thd, NDBDICT *dict, KEY *key_info, index= dict->getIndexGlobal(unique_index_name, *m_table); if (!index) ERR_RETURN(dict->getNdbError()); - DBUG_PRINT("info", ("index: 0x%x id: %d version: %d.%d status: %d", - index, + DBUG_PRINT("info", ("index: 0x%lx id: %d version: %d.%d status: %d", + (long) index, index->getObjectId(), index->getObjectVersion() & 0xFFFFFF, index->getObjectVersion() >> 24, @@ -1236,7 +1240,6 @@ int ha_ndbcluster::open_indexes(Ndb *ndb, TABLE *tab, bool ignore_error) int error= 0; THD *thd=current_thd; NDBDICT *dict= ndb->getDictionary(); - const char *index_name; KEY* key_info= tab->key_info; const char **key_name= tab->s->keynames.type_names; DBUG_ENTER("ha_ndbcluster::open_indexes"); @@ -1248,6 +1251,9 @@ int ha_ndbcluster::open_indexes(Ndb *ndb, TABLE *tab, bool ignore_error) m_index[i].index= m_index[i].unique_index= NULL; else break; + m_index[i].null_in_unique_index= FALSE; + if (check_index_fields_not_null(key_info)) + m_index[i].null_in_unique_index= TRUE; } if (error && !ignore_error) @@ -1283,7 +1289,6 @@ void ha_ndbcluster::renumber_indexes(Ndb *ndb, TABLE *tab) const char *index_name; KEY* key_info= tab->key_info; const char **key_name= tab->s->keynames.type_names; - NDBDICT *dict= ndb->getDictionary(); DBUG_ENTER("ha_ndbcluster::renumber_indexes"); for (i= 0; i < tab->s->keys; i++, key_info++, key_name++) @@ -1390,9 +1395,8 @@ NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_key(uint inx, ORDERED_INDEX); } -int ha_ndbcluster::check_index_fields_not_null(uint inx) +bool ha_ndbcluster::check_index_fields_not_null(KEY* key_info) { - KEY* key_info= table->key_info + inx; KEY_PART_INFO* key_part= key_info->key_part; KEY_PART_INFO* end= key_part+key_info->key_parts; DBUG_ENTER("ha_ndbcluster::check_index_fields_not_null"); @@ -1401,14 +1405,10 @@ int ha_ndbcluster::check_index_fields_not_null(uint inx) { Field* field= key_part->field; if (field->maybe_null()) - { - my_printf_error(ER_NULL_COLUMN_IN_INDEX,ER(ER_NULL_COLUMN_IN_INDEX), - MYF(0),field->field_name); - DBUG_RETURN(ER_NULL_COLUMN_IN_INDEX); - } + DBUG_RETURN(TRUE); } - DBUG_RETURN(0); + DBUG_RETURN(FALSE); } void ha_ndbcluster::release_metadata(THD *thd, Ndb *ndb) @@ -1508,6 +1508,12 @@ inline NDB_INDEX_TYPE ha_ndbcluster::get_index_type(uint idx_no) const return m_index[idx_no].type; } +inline bool ha_ndbcluster::has_null_in_unique_index(uint idx_no) const +{ + DBUG_ASSERT(idx_no < MAX_KEY); + return m_index[idx_no].null_in_unique_index; +} + /* Get the flags for an index @@ -1720,7 +1726,8 @@ int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf, ERR_RETURN(trans->getNdbError()); } - if (execute_no_commit_ie(this,trans,false) != 0) + if ((res = execute_no_commit_ie(this,trans,FALSE)) != 0 || + op->getNdbError().code) { table->status= STATUS_NOT_FOUND; DBUG_RETURN(ndb_err(trans)); @@ -1785,7 +1792,7 @@ int ha_ndbcluster::complemented_read(const byte *old_data, byte *new_data, } } - if (execute_no_commit(this,trans,false) != 0) + if (execute_no_commit(this,trans,FALSE) != 0) { table->status= STATUS_NOT_FOUND; DBUG_RETURN(ndb_err(trans)); @@ -1831,7 +1838,7 @@ bool ha_ndbcluster::check_all_operations_for_error(NdbTransaction *trans, if (err.status != NdbError::Success) { if (ndb_to_mysql_error(&err) != (int) errcode) - DBUG_RETURN(false); + DBUG_RETURN(FALSE); if (op == last) break; op= trans->getNextCompletedOperation(op); } @@ -1862,10 +1869,10 @@ bool ha_ndbcluster::check_all_operations_for_error(NdbTransaction *trans, if (errcode == HA_ERR_KEY_NOT_FOUND) m_dupkey= table->s->primary_key; } - DBUG_RETURN(false); + DBUG_RETURN(FALSE); } } - DBUG_RETURN(true); + DBUG_RETURN(TRUE); } @@ -1874,7 +1881,8 @@ bool ha_ndbcluster::check_all_operations_for_error(NdbTransaction *trans, * primary key or unique index values */ -int ha_ndbcluster::peek_indexed_rows(const byte *record) +int ha_ndbcluster::peek_indexed_rows(const byte *record, + bool check_pk) { NdbTransaction *trans= m_active_trans; NdbOperation *op; @@ -1883,9 +1891,10 @@ int ha_ndbcluster::peek_indexed_rows(const byte *record) int res; DBUG_ENTER("peek_indexed_rows"); - NdbOperation::LockMode lm= NdbOperation::LM_Read; + NdbOperation::LockMode lm= + (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type); first= NULL; - if (table->s->primary_key != MAX_KEY) + if (check_pk && table->s->primary_key != MAX_KEY) { /* * Fetch any row with colliding primary key @@ -1941,7 +1950,7 @@ int ha_ndbcluster::peek_indexed_rows(const byte *record) } last= trans->getLastDefinedOperation(); if (first) - res= execute_no_commit_ie(this,trans,false); + res= execute_no_commit_ie(this,trans,FALSE); else { // Table has no keys @@ -1990,7 +1999,8 @@ int ha_ndbcluster::unique_index_read(const byte *key, if ((res= define_read_attrs(buf, op))) DBUG_RETURN(res); - if (execute_no_commit_ie(this,trans,false) != 0) + if (execute_no_commit_ie(this,trans,FALSE) != 0 || + op->getNdbError().code) { table->status= STATUS_NOT_FOUND; DBUG_RETURN(ndb_err(trans)); @@ -2004,7 +2014,7 @@ int ha_ndbcluster::unique_index_read(const byte *key, inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor) { DBUG_ENTER("fetch_next"); - int check; + int local_check; NdbTransaction *trans= m_active_trans; if (m_lock_tuple) @@ -2015,19 +2025,21 @@ inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor) LOCK WITH SHARE MODE) and row was not explictly unlocked with unlock_row() call */ - NdbConnection *trans= m_active_trans; + NdbConnection *con_trans= m_active_trans; NdbOperation *op; // Lock row DBUG_PRINT("info", ("Keeping lock on scanned row")); if (!(op= m_active_cursor->lockCurrentTuple())) { - m_lock_tuple= false; - ERR_RETURN(trans->getNdbError()); + /* purecov: begin inspected */ + m_lock_tuple= FALSE; + ERR_RETURN(con_trans->getNdbError()); + /* purecov: end */ } m_ops_pending++; } - m_lock_tuple= false; + m_lock_tuple= FALSE; bool contact_ndb= m_lock.type < TL_WRITE_ALLOW_WRITE && m_lock.type != TL_READ_WITH_SHARED_LOCKS;; @@ -2038,13 +2050,13 @@ inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor) */ if (m_ops_pending && m_blobs_pending) { - if (execute_no_commit(this,trans,false) != 0) + if (execute_no_commit(this,trans,FALSE) != 0) DBUG_RETURN(ndb_err(trans)); m_ops_pending= 0; m_blobs_pending= FALSE; } - if ((check= cursor->nextResult(contact_ndb, m_force_send)) == 0) + if ((local_check= cursor->nextResult(contact_ndb, m_force_send)) == 0) { /* Explicitly lock tuple if "select for update" or @@ -2055,7 +2067,7 @@ inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor) m_lock.type == TL_READ_WITH_SHARED_LOCKS); DBUG_RETURN(0); } - else if (check == 1 || check == 2) + else if (local_check == 1 || local_check == 2) { // 1: No more records // 2: No more cached records @@ -2065,12 +2077,12 @@ inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor) all pending update or delete operations should be sent to NDB */ - DBUG_PRINT("info", ("ops_pending: %d", m_ops_pending)); + DBUG_PRINT("info", ("ops_pending: %ld", (long) m_ops_pending)); if (m_ops_pending) { if (m_transaction_on) { - if (execute_no_commit(this,trans,false) != 0) + if (execute_no_commit(this,trans,FALSE) != 0) DBUG_RETURN(-1); } else @@ -2085,13 +2097,13 @@ inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor) } m_ops_pending= 0; } - contact_ndb= (check == 2); + contact_ndb= (local_check == 2); } else { DBUG_RETURN(-1); } - } while (check == 2); + } while (local_check == 2); DBUG_RETURN(1); } @@ -2268,8 +2280,7 @@ int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op, DBUG_PRINT("error", ("key %d unknown flag %d", j, p.key->flag)); DBUG_ASSERT(FALSE); // Stop setting bounds but continue with what we have - op->end_of_bound(range_no); - DBUG_RETURN(0); + DBUG_RETURN(op->end_of_bound(range_no)); } } } @@ -2298,7 +2309,7 @@ int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op, // Set bound if not done with this key if (p.key != NULL) { - DBUG_PRINT("info", ("key %d:%d offset=%d length=%d last=%d bound=%d", + DBUG_PRINT("info", ("key %d:%d offset: %d length: %d last: %d bound: %d", j, i, tot_len, part_len, p.part_last, p.bound_type)); DBUG_DUMP("info", (const char*)p.part_ptr, part_store_len); @@ -2316,8 +2327,7 @@ int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op, tot_len+= part_store_len; } - op->end_of_bound(range_no); - DBUG_RETURN(0); + DBUG_RETURN(op->end_of_bound(range_no)); } /* @@ -2351,7 +2361,7 @@ int ha_ndbcluster::ordered_index_scan(const key_range *start_key, bool need_pk = (lm == NdbOperation::LM_Read); if (!(op= trans->getNdbIndexScanOperation(m_index[active_index].index, m_table)) || - op->readTuples(lm, 0, parallelism, sorted, descending, false, need_pk)) + op->readTuples(lm, 0, parallelism, sorted, descending, FALSE, need_pk)) ERR_RETURN(trans->getNdbError()); if (m_use_partition_function && part_spec != NULL && part_spec->start_part == part_spec->end_part) @@ -2373,7 +2383,7 @@ int ha_ndbcluster::ordered_index_scan(const key_range *start_key, { const key_range *keys[2]= { start_key, end_key }; - res= set_bounds(op, active_index, false, keys); + res= set_bounds(op, active_index, FALSE, keys); if (res) DBUG_RETURN(res); } @@ -2397,7 +2407,7 @@ int ha_ndbcluster::ordered_index_scan(const key_range *start_key, ERR_RETURN(trans->getNdbError()); } - if (execute_no_commit(this,trans,false) != 0) + if (execute_no_commit(this,trans,FALSE) != 0) DBUG_RETURN(ndb_err(trans)); DBUG_RETURN(next_result(buf)); @@ -2427,6 +2437,78 @@ guess_scan_flags(NdbOperation::LockMode lm, return flags; } + +/* + Unique index scan in NDB (full table scan with scan filter) + */ + +int ha_ndbcluster::unique_index_scan(const KEY* key_info, + const byte *key, + uint key_len, + byte *buf) +{ + int res; + NdbScanOperation *op; + NdbTransaction *trans= m_active_trans; + part_id_range part_spec; + + DBUG_ENTER("unique_index_scan"); + DBUG_PRINT("enter", ("Starting new scan on %s", m_tabname)); + + NdbOperation::LockMode lm= + (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type); + int flags= guess_scan_flags(lm, m_table, table->read_set); + if (!(op=trans->getNdbScanOperation((const NDBTAB *) m_table)) || + op->readTuples(lm, flags, parallelism)) + ERR_RETURN(trans->getNdbError()); + m_active_cursor= op; + + if (m_use_partition_function) + { + part_spec.start_part= 0; + part_spec.end_part= m_part_info->get_tot_partitions() - 1; + prune_partition_set(table, &part_spec); + DBUG_PRINT("info", ("part_spec.start_part = %u, part_spec.end_part = %u", + part_spec.start_part, part_spec.end_part)); + /* + If partition pruning has found no partition in set + we can return HA_ERR_END_OF_FILE + If partition pruning has found exactly one partition in set + we can optimize scan to run towards that partition only. + */ + if (part_spec.start_part > part_spec.end_part) + { + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + else if (part_spec.start_part == part_spec.end_part) + { + /* + Only one partition is required to scan, if sorted is required we + don't need it any more since output from one ordered partitioned + index is always sorted. + */ + m_active_cursor->setPartitionId(part_spec.start_part); + } + // If table has user defined partitioning + // and no primary key, we need to read the partition id + // to support ORDER BY queries + if ((table_share->primary_key == MAX_KEY) && + (get_ndb_partition_id(op))) + ERR_RETURN(trans->getNdbError()); + } + + if (generate_scan_filter_from_key(op, key_info, key, key_len, buf)) + DBUG_RETURN(ndb_err(trans)); + if ((res= define_read_attrs(buf, op))) + DBUG_RETURN(res); + + if (execute_no_commit(this,trans,FALSE) != 0) + DBUG_RETURN(ndb_err(trans)); + DBUG_PRINT("exit", ("Scan started successfully")); + DBUG_RETURN(next_result(buf)); +} + + /* Start full table scan in NDB */ @@ -2455,7 +2537,7 @@ int ha_ndbcluster::full_table_scan(byte *buf) part_spec.start_part= 0; part_spec.end_part= m_part_info->get_tot_partitions() - 1; prune_partition_set(table, &part_spec); - DBUG_PRINT("info", ("part_spec.start_part = %u, part_spec.end_part = %u", + DBUG_PRINT("info", ("part_spec.start_part: %u part_spec.end_part: %u", part_spec.start_part, part_spec.end_part)); /* If partition pruning has found no partition in set @@ -2489,7 +2571,7 @@ int ha_ndbcluster::full_table_scan(byte *buf) if ((res= define_read_attrs(buf, op))) DBUG_RETURN(res); - if (execute_no_commit(this,trans,false) != 0) + if (execute_no_commit(this,trans,FALSE) != 0) DBUG_RETURN(ndb_err(trans)); DBUG_PRINT("exit", ("Scan started successfully")); DBUG_RETURN(next_result(buf)); @@ -2505,7 +2587,7 @@ int ha_ndbcluster::write_row(byte *record) NdbTransaction *trans= m_active_trans; NdbOperation *op; int res; - THD *thd= current_thd; + THD *thd= table->in_use; longlong func_value= 0; DBUG_ENTER("ha_ndbcluster::write_row"); @@ -2518,7 +2600,6 @@ int ha_ndbcluster::write_row(byte *record) */ if (has_auto_increment) { - THD *thd= table->in_use; int error; m_skip_auto_increment= FALSE; @@ -2538,7 +2619,7 @@ int ha_ndbcluster::write_row(byte *record) start_bulk_insert will set parameters to ensure that each write_row is committed individually */ - int peek_res= peek_indexed_rows(record); + int peek_res= peek_indexed_rows(record, TRUE); if (!peek_res) { @@ -2651,13 +2732,13 @@ int ha_ndbcluster::write_row(byte *record) { // Send rows to NDB DBUG_PRINT("info", ("Sending inserts to NDB, "\ - "rows_inserted:%d, bulk_insert_rows: %d", + "rows_inserted: %d bulk_insert_rows: %d", (int)m_rows_inserted, (int)m_bulk_insert_rows)); m_bulk_insert_not_flushed= FALSE; if (m_transaction_on) { - if (execute_no_commit(this,trans,false) != 0) + if (execute_no_commit(this,trans,FALSE) != 0) { m_skip_auto_increment= TRUE; no_uncommitted_rows_execute_failure(); @@ -2683,10 +2764,12 @@ int ha_ndbcluster::write_row(byte *record) { Ndb *ndb= get_ndb(); Uint64 next_val= (Uint64) table->next_number_field->val_int() + 1; +#ifndef DBUG_OFF char buff[22]; DBUG_PRINT("info", ("Trying to set next auto increment value to %s", llstr(next_val, buff))); +#endif Ndb_tuple_id_range_guard g(m_share); if (ndb->setAutoIncrementValue(m_table, g.range, next_val, TRUE) == -1) @@ -2739,7 +2822,7 @@ int ha_ndbcluster::key_cmp(uint keynr, const byte * old_row, int ha_ndbcluster::update_row(const byte *old_data, byte *new_data) { - THD *thd= current_thd; + THD *thd= table->in_use; NdbTransaction *trans= m_active_trans; NdbScanOperation* cursor= m_active_cursor; NdbOperation *op; @@ -2747,9 +2830,27 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data) uint32 old_part_id= 0, new_part_id= 0; int error; longlong func_value; + bool pk_update= (table_share->primary_key != MAX_KEY && + key_cmp(table_share->primary_key, old_data, new_data)); DBUG_ENTER("update_row"); m_write_op= TRUE; + /* + * If IGNORE the ignore constraint violations on primary and unique keys, + * but check that it is not part of INSERT ... ON DUPLICATE KEY UPDATE + */ + if (m_ignore_dup_key && thd->lex->sql_command == SQLCOM_UPDATE) + { + int peek_res= peek_indexed_rows(new_data, pk_update); + + if (!peek_res) + { + DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY); + } + if (peek_res != HA_ERR_KEY_NOT_FOUND) + DBUG_RETURN(peek_res); + } + statistic_increment(thd->status_var.ha_update_count, &LOCK_status); if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) { @@ -2770,9 +2871,7 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data) * Check for update of primary key or partition change * for special handling */ - if (((table_share->primary_key != MAX_KEY) && - key_cmp(table_share->primary_key, old_data, new_data)) || - (old_part_id != new_part_id)) + if (pk_update || old_part_id != new_part_id) { int read_res, insert_res, delete_res, undo_res; @@ -2832,7 +2931,7 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data) DBUG_PRINT("info", ("Calling updateTuple on cursor")); if (!(op= cursor->updateCurrentTuple())) ERR_RETURN(trans->getNdbError()); - m_lock_tuple= false; + m_lock_tuple= FALSE; m_ops_pending++; if (uses_blob_value()) m_blobs_pending= TRUE; @@ -2895,7 +2994,7 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data) op->setValue(no_fields, part_func_value); } // Execute update operation - if (!cursor && execute_no_commit(this,trans,false) != 0) { + if (!cursor && execute_no_commit(this,trans,FALSE) != 0) { no_uncommitted_rows_execute_failure(); DBUG_RETURN(ndb_err(trans)); } @@ -2910,7 +3009,7 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data) int ha_ndbcluster::delete_row(const byte *record) { - THD *thd= current_thd; + THD *thd= table->in_use; NdbTransaction *trans= m_active_trans; NdbScanOperation* cursor= m_active_cursor; NdbOperation *op; @@ -2941,7 +3040,7 @@ int ha_ndbcluster::delete_row(const byte *record) DBUG_PRINT("info", ("Calling deleteTuple on cursor")); if (cursor->deleteCurrentTuple() != 0) ERR_RETURN(trans->getNdbError()); - m_lock_tuple= false; + m_lock_tuple= FALSE; m_ops_pending++; if (m_use_partition_function) @@ -2981,7 +3080,7 @@ int ha_ndbcluster::delete_row(const byte *record) } // Execute delete operation - if (execute_no_commit(this,trans,false) != 0) { + if (execute_no_commit(this,trans,FALSE) != 0) { no_uncommitted_rows_execute_failure(); DBUG_RETURN(ndb_err(trans)); } @@ -3101,7 +3200,8 @@ void ndb_unpack_record(TABLE *table, NdbValue *value, char* ptr; field_blob->get_ptr(&ptr, row_offset); uint32 len= field_blob->get_length(row_offset); - DBUG_PRINT("info",("[%u] SET ptr=%p len=%u", col_no, ptr, len)); + DBUG_PRINT("info",("[%u] SET ptr: 0x%lx len: %u", + col_no, (long) ptr, len)); #endif } } @@ -3208,8 +3308,7 @@ int ha_ndbcluster::index_init(uint index, bool sorted) unless m_lock.type == TL_READ_HIGH_PRIORITY and no sub-sequent call to unlock_row() */ - m_lock_tuple= false; - m_lock_tuple= false; + m_lock_tuple= FALSE; DBUG_RETURN(0); } @@ -3231,7 +3330,6 @@ check_null_in_key(const KEY* key_info, const byte *key, uint key_len) const byte* end_ptr= key + key_len; curr_part= key_info->key_part; end_part= curr_part + key_info->key_parts; - for (; curr_part != end_part && key < end_ptr; curr_part++) { @@ -3343,7 +3441,7 @@ int ha_ndbcluster::read_range_first_to_buf(const key_range *start_key, if (m_use_partition_function) { get_partition_set(table, buf, active_index, start_key, &part_spec); - DBUG_PRINT("info", ("part_spec.start_part = %u, part_spec.end_part = %u", + DBUG_PRINT("info", ("part_spec.start_part: %u part_spec.end_part: %u", part_spec.start_part, part_spec.end_part)); /* If partition pruning has found no partition in set @@ -3376,8 +3474,9 @@ int ha_ndbcluster::read_range_first_to_buf(const key_range *start_key, { if (m_active_cursor && (error= close_scan())) DBUG_RETURN(error); - DBUG_RETURN(pk_read(start_key->key, start_key->length, buf, - part_spec.start_part)); + error= pk_read(start_key->key, start_key->length, buf, + part_spec.start_part); + DBUG_RETURN(error == HA_ERR_KEY_NOT_FOUND ? HA_ERR_END_OF_FILE : error); } break; case UNIQUE_ORDERED_INDEX: @@ -3388,8 +3487,15 @@ int ha_ndbcluster::read_range_first_to_buf(const key_range *start_key, { if (m_active_cursor && (error= close_scan())) DBUG_RETURN(error); - DBUG_RETURN(unique_index_read(start_key->key, start_key->length, buf)); + + error= unique_index_read(start_key->key, start_key->length, buf); + DBUG_RETURN(error == HA_ERR_KEY_NOT_FOUND ? HA_ERR_END_OF_FILE : error); } + else if (type == UNIQUE_INDEX) + DBUG_RETURN(unique_index_scan(key_info, + start_key->key, + start_key->length, + buf)); break; default: break; @@ -3461,20 +3567,20 @@ int ha_ndbcluster::close_scan() if (!(op= cursor->lockCurrentTuple())) { - m_lock_tuple= false; + m_lock_tuple= FALSE; ERR_RETURN(trans->getNdbError()); } m_ops_pending++; } - m_lock_tuple= false; + m_lock_tuple= FALSE; if (m_ops_pending) { /* Take over any pending transactions to the deleteing/updating transaction before closing the scan */ - DBUG_PRINT("info", ("ops_pending: %d", m_ops_pending)); - if (execute_no_commit(this,trans,false) != 0) { + DBUG_PRINT("info", ("ops_pending: %ld", (long) m_ops_pending)); + if (execute_no_commit(this,trans,FALSE) != 0) { no_uncommitted_rows_execute_failure(); DBUG_RETURN(ndb_err(trans)); } @@ -3592,20 +3698,26 @@ void ha_ndbcluster::position(const byte *record) size_t len = key_part->length; const byte * ptr = record + key_part->offset; Field *field = key_part->field; - if ((field->type() == MYSQL_TYPE_VARCHAR) && - ((Field_varstring*)field)->length_bytes == 1) + if (field->type() == MYSQL_TYPE_VARCHAR) { - /** - * Keys always use 2 bytes length - */ - buff[0] = ptr[0]; - buff[1] = 0; - memcpy(buff+2, ptr + 1, len); - len += 2; + if (((Field_varstring*)field)->length_bytes == 1) + { + /** + * Keys always use 2 bytes length + */ + buff[0] = ptr[0]; + buff[1] = 0; + memcpy(buff+2, ptr + 1, len); + } + else + { + memcpy(buff, ptr, len + 2); + } + len += 2; } else { - memcpy(buff, ptr, len); + memcpy(buff, ptr, len); } buff += len; } @@ -3642,8 +3754,9 @@ void ha_ndbcluster::position(const byte *record) } -void ha_ndbcluster::info(uint flag) +int ha_ndbcluster::info(uint flag) { + int result= 0; DBUG_ENTER("info"); DBUG_PRINT("enter", ("flag: %d", flag)); @@ -3661,18 +3774,19 @@ void ha_ndbcluster::info(uint flag) if (m_ha_not_exact_count) stats.records= 100; else - records_update(); + result= records_update(); } else { if ((my_errno= check_ndb_connection())) - DBUG_VOID_RETURN; + DBUG_RETURN(my_errno); Ndb *ndb= get_ndb(); ndb->setDatabaseName(m_dbname); struct Ndb_statistics stat; ndb->setDatabaseName(m_dbname); if (current_thd->variables.ndb_use_exact_count && - ndb_get_table_statistics(ndb, m_table, &stat) == 0) + (result= ndb_get_table_statistics(this, TRUE, ndb, m_table, &stat)) + == 0) { stats.mean_rec_length= stat.row_size; stats.data_file_length= stat.fragment_memory; @@ -3698,7 +3812,7 @@ void ha_ndbcluster::info(uint flag) if (flag & HA_STATUS_AUTO) { DBUG_PRINT("info", ("HA_STATUS_AUTO")); - if (m_table) + if (m_table && table->found_next_number_field) { Ndb *ndb= get_ndb(); Ndb_tuple_id_range_guard g(m_share); @@ -3716,7 +3830,11 @@ void ha_ndbcluster::info(uint flag) stats.auto_increment_value= (ulonglong)auto_increment_value64; } } - DBUG_VOID_RETURN; + + if(result == -1) + result= HA_ERR_NO_CONNECTION; + + DBUG_RETURN(result); } @@ -3863,12 +3981,12 @@ int ha_ndbcluster::end_bulk_insert() NdbTransaction *trans= m_active_trans; // Send rows to NDB DBUG_PRINT("info", ("Sending inserts to NDB, "\ - "rows_inserted:%d, bulk_insert_rows: %d", + "rows_inserted: %d bulk_insert_rows: %d", (int) m_rows_inserted, (int) m_bulk_insert_rows)); m_bulk_insert_not_flushed= FALSE; if (m_transaction_on) { - if (execute_no_commit(this, trans,false) != 0) + if (execute_no_commit(this, trans,FALSE) != 0) { no_uncommitted_rows_execute_failure(); my_errno= error= ndb_err(trans); @@ -3883,7 +4001,7 @@ int ha_ndbcluster::end_bulk_insert() } else { - int res= trans->restart(); + IF_DBUG(int res=) trans->restart(); DBUG_ASSERT(res == 0); } } @@ -4193,7 +4311,7 @@ void ha_ndbcluster::unlock_row() DBUG_ENTER("unlock_row"); DBUG_PRINT("info", ("Unlocking row")); - m_lock_tuple= false; + m_lock_tuple= FALSE; DBUG_VOID_RETURN; } @@ -4221,11 +4339,10 @@ int ha_ndbcluster::start_stmt(THD *thd, thr_lock_type lock_type) ERR_RETURN(ndb->getNdbError()); no_uncommitted_rows_reset(thd); thd_ndb->stmt= trans; + thd_ndb->query_state&= NDB_QUERY_NORMAL; trans_register_ha(thd, FALSE, ndbcluster_hton); } - thd_ndb->query_state&= NDB_QUERY_NORMAL; m_active_trans= trans; - // Start of statement m_ops_pending= 0; thd->set_current_stmt_binlog_row_based_if_mixed(); @@ -4273,8 +4390,8 @@ static int ndbcluster_commit(handlerton *hton, THD *thd, bool all) while ((share= it++)) { pthread_mutex_lock(&share->mutex); - DBUG_PRINT("info", ("Invalidate commit_count for %s, share->commit_count: %d ", - share->key, share->commit_count)); + DBUG_PRINT("info", ("Invalidate commit_count for %s, share->commit_count: %lu", + share->table_name, (ulong) share->commit_count)); share->commit_count= 0; share->commit_count_lock++; pthread_mutex_unlock(&share->mutex); @@ -4523,19 +4640,29 @@ static int create_ndb_column(NDBCOL &col, col.setType(NDBCOL::Text); col.setCharset(cs); } - // Use "<=" even if "<" is the exact condition - if (field->max_length() <= (1 << 8)) - goto mysql_type_tiny_blob; - else if (field->max_length() <= (1 << 16)) { - col.setInlineSize(256); - col.setPartSize(2000); - col.setStripeSize(16); + Field_blob *field_blob= (Field_blob *)field; + /* + * max_data_length is 2^8-1, 2^16-1, 2^24-1 for tiny, blob, medium. + * Tinyblob gets no blob parts. The other cases are just a crude + * way to control part size and striping. + * + * In mysql blob(256) is promoted to blob(65535) so it does not + * in fact fit "inline" in NDB. + */ + if (field_blob->max_data_length() < (1 << 8)) + goto mysql_type_tiny_blob; + else if (field_blob->max_data_length() < (1 << 16)) + { + col.setInlineSize(256); + col.setPartSize(2000); + col.setStripeSize(16); + } + else if (field_blob->max_data_length() < (1 << 24)) + goto mysql_type_medium_blob; + else + goto mysql_type_long_blob; } - else if (field->max_length() <= (1 << 24)) - goto mysql_type_medium_blob; - else - goto mysql_type_long_blob; break; mysql_type_medium_blob: case MYSQL_TYPE_MEDIUM_BLOB: @@ -4592,7 +4719,9 @@ static int create_ndb_column(NDBCOL &col, // Set autoincrement if (field->flags & AUTO_INCREMENT_FLAG) { +#ifndef DBUG_OFF char buff[22]; +#endif col.setAutoIncrement(TRUE); ulonglong value= info->auto_increment_value ? info->auto_increment_value : (ulonglong) 1; @@ -4610,15 +4739,16 @@ static int create_ndb_column(NDBCOL &col, int ha_ndbcluster::create(const char *name, TABLE *form, - HA_CREATE_INFO *info) + HA_CREATE_INFO *create_info) { THD *thd= current_thd; NDBTAB tab; NDBCOL col; uint pack_length, length, i, pk_length= 0; const void *data, *pack_data; - bool create_from_engine= (info->table_options & HA_OPTION_CREATE_FROM_ENGINE); + bool create_from_engine= (create_info->table_options & HA_OPTION_CREATE_FROM_ENGINE); bool is_truncate= (thd->lex->sql_command == SQLCOM_TRUNCATE); + char tablespace[FN_LEN]; DBUG_ENTER("ha_ndbcluster::create"); DBUG_PRINT("enter", ("name: %s", name)); @@ -4627,8 +4757,22 @@ int ha_ndbcluster::create(const char *name, set_dbname(name); set_tabname(name); + if ((my_errno= check_ndb_connection())) + DBUG_RETURN(my_errno); + + Ndb *ndb= get_ndb(); + NDBDICT *dict= ndb->getDictionary(); + if (is_truncate) { + { + Ndb_table_guard ndbtab_g(dict, m_tabname); + if (!(m_table= ndbtab_g.get_table())) + ERR_RETURN(dict->getNdbError()); + if ((get_tablespace_name(thd, tablespace, FN_LEN))) + create_info->tablespace= tablespace; + m_table= NULL; + } DBUG_PRINT("info", ("Dropping and re-creating table for TRUNCATE")); if ((my_errno= delete_table(name))) DBUG_RETURN(my_errno); @@ -4656,7 +4800,7 @@ int ha_ndbcluster::create(const char *name, schema distribution table is setup ( unless it is a creation of the schema dist table itself ) */ - if (!schema_share && + if (!ndb_schema_share && !(strcmp(m_dbname, NDB_REP_DB) == 0 && strcmp(m_tabname, NDB_SCHEMA_TABLE) == 0)) { @@ -4667,7 +4811,7 @@ int ha_ndbcluster::create(const char *name, DBUG_PRINT("table", ("name: %s", m_tabname)); tab.setName(m_tabname); - tab.setLogging(!(info->options & HA_LEX_CREATE_TMP_TABLE)); + tab.setLogging(!(create_info->options & HA_LEX_CREATE_TMP_TABLE)); // Save frm data for this table if (readfrm(name, &data, &length)) @@ -4677,8 +4821,7 @@ int ha_ndbcluster::create(const char *name, my_free((char*)data, MYF(0)); DBUG_RETURN(2); } - - DBUG_PRINT("info", ("setFrm data=%lx len=%d", pack_data, pack_length)); + DBUG_PRINT("info", ("setFrm data: 0x%lx len: %d", (long) pack_data, pack_length)); tab.setFrm(pack_data, pack_length); my_free((char*)data, MYF(0)); my_free((char*)pack_data, MYF(0)); @@ -4689,10 +4832,10 @@ int ha_ndbcluster::create(const char *name, DBUG_PRINT("info", ("name: %s, type: %u, pack_length: %d", field->field_name, field->real_type(), field->pack_length())); - if ((my_errno= create_ndb_column(col, field, info))) + if ((my_errno= create_ndb_column(col, field, create_info))) DBUG_RETURN(my_errno); - if (info->store_on_disk || getenv("NDB_DEFAULT_DISK")) + if (create_info->storage_media == HA_SM_DISK) col.setStorageType(NdbDictionary::Column::StorageTypeDisk); else col.setStorageType(NdbDictionary::Column::StorageTypeMemory); @@ -4712,11 +4855,29 @@ int ha_ndbcluster::create(const char *name, NdbDictionary::Column::StorageTypeMemory); } - if (info->store_on_disk) - if (info->tablespace) - tab.setTablespace(info->tablespace); + if (create_info->storage_media == HA_SM_DISK) + { + if (create_info->tablespace) + tab.setTablespaceName(create_info->tablespace); else - tab.setTablespace("DEFAULT-TS"); + tab.setTablespaceName("DEFAULT-TS"); + } + else if (create_info->tablespace) + { + if (create_info->storage_media == HA_SM_MEMORY) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_ILLEGAL_HA_CREATE_OPTION, + ER(ER_ILLEGAL_HA_CREATE_OPTION), + ndbcluster_hton_name, + "TABLESPACE currently only supported for " + "STORAGE DISK"); + DBUG_RETURN(HA_ERR_UNSUPPORTED); + } + tab.setTablespaceName(create_info->tablespace); + create_info->storage_media = HA_SM_DISK; //if use tablespace, that also means store on disk + } + // No primary key, create shadow key as 64 bit, auto increment if (form->s->primary_key == MAX_KEY) { @@ -4745,13 +4906,13 @@ int ha_ndbcluster::create(const char *name, case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: { - NdbDictionary::Column * col= tab.getColumn(i); - int size= pk_length + (col->getPartSize()+3)/4 + 7; + NdbDictionary::Column * column= tab.getColumn(i); + int size= pk_length + (column->getPartSize()+3)/4 + 7; if (size > NDB_MAX_TUPLE_SIZE_IN_WORDS && (pk_length+7) < NDB_MAX_TUPLE_SIZE_IN_WORDS) { size= NDB_MAX_TUPLE_SIZE_IN_WORDS - pk_length - 7; - col->setPartSize(4*size); + column->setPartSize(4*size); } /** * If size > NDB_MAX and pk_length+7 >= NDB_MAX @@ -4771,12 +4932,7 @@ int ha_ndbcluster::create(const char *name, DBUG_RETURN(my_errno); } - if ((my_errno= check_ndb_connection())) - DBUG_RETURN(my_errno); - // Create the table in NDB - Ndb *ndb= get_ndb(); - NDBDICT *dict= ndb->getDictionary(); if (dict->createTable(tab) != 0) { const NdbError err= dict->getNdbError(); @@ -4849,11 +5005,17 @@ int ha_ndbcluster::create(const char *name, get a new share */ - if (!(share= get_share(name, form, true, true))) + /* ndb_share reference create */ + if (!(share= get_share(name, form, TRUE, TRUE))) { sql_print_error("NDB: allocating table share for %s failed", name); /* my_errno is set */ } + else + { + DBUG_PRINT("NDB_SHARE", ("%s binlog create use_count: %u", + share->key, share->use_count)); + } pthread_mutex_unlock(&ndbcluster_mutex); while (!IS_TMP_PREFIX(m_tabname)) @@ -4862,7 +5024,7 @@ int ha_ndbcluster::create(const char *name, ndb_rep_event_name(&event_name,m_dbname,m_tabname); int do_event_op= ndb_binlog_running; - if (!schema_share && + if (!ndb_schema_share && strcmp(share->db, NDB_REP_DB) == 0 && strcmp(share->table_name, NDB_SCHEMA_TABLE) == 0) do_event_op= 1; @@ -4877,7 +5039,7 @@ int ha_ndbcluster::create(const char *name, if (ndb_extra_logging) sql_print_information("NDB Binlog: CREATE TABLE Event: %s", event_name.c_ptr()); - if (share && do_event_op && + if (share && ndbcluster_create_event_ops(share, m_table, event_name.c_ptr())) { sql_print_error("NDB Binlog: FAILED CREATE TABLE event operations." @@ -4911,10 +5073,8 @@ int ha_ndbcluster::create(const char *name, int ha_ndbcluster::create_handler_files(const char *file, const char *old_name, int action_flag, - HA_CREATE_INFO *info) + HA_CREATE_INFO *create_info) { - char path[FN_REFLEN]; - const char *name; Ndb* ndb; const NDBTAB *tab; const void *data, *pack_data; @@ -4932,7 +5092,7 @@ int ha_ndbcluster::create_handler_files(const char *file, DBUG_RETURN(HA_ERR_NO_CONNECTION); NDBDICT *dict= ndb->getDictionary(); - if (!info->frm_only) + if (!create_info->frm_only) DBUG_RETURN(0); // Must be a create, ignore since frm is saved in create // TODO handle this @@ -4969,6 +5129,9 @@ int ha_ndbcluster::create_handler_files(const char *file, } set_ndb_share_state(m_share, NSS_INITIAL); + /* ndb_share reference schema(?) free */ + DBUG_PRINT("NDB_SHARE", ("%s binlog schema(?) free use_count: %u", + m_share->key, m_share->use_count)); free_share(&m_share); // Decrease ref_count DBUG_RETURN(error); @@ -5002,10 +5165,26 @@ int ha_ndbcluster::create_index(const char *name, KEY *key_info, error= create_unique_index(unique_name, key_info); break; case UNIQUE_INDEX: - if (!(error= check_index_fields_not_null(idx_no))) - error= create_unique_index(unique_name, key_info); + if (check_index_fields_not_null(key_info)) + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_NULL_COLUMN_IN_INDEX, + "Ndb does not support unique index on NULL valued attributes, index access with NULL value will become full table scan"); + } + error= create_unique_index(unique_name, key_info); break; case ORDERED_INDEX: + if (key_info->algorithm == HA_KEY_ALG_HASH) + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_ILLEGAL_HA_CREATE_OPTION, + ER(ER_ILLEGAL_HA_CREATE_OPTION), + ndbcluster_hton_name, + "Ndb does not support non-unique " + "hash based indexes"); + error= HA_ERR_UNSUPPORTED; + break; + } error= create_ordered_index(name, key_info); break; default: @@ -5079,7 +5258,10 @@ int ha_ndbcluster::create_ndb_index(const char *name, */ void ha_ndbcluster::prepare_for_alter() { + /* ndb_share reference schema */ ndbcluster_get_share(m_share); // Increase ref_count + DBUG_PRINT("NDB_SHARE", ("%s binlog schema use_count: %u", + m_share->key, m_share->use_count)); set_ndb_share_state(m_share, NSS_ALTERED); } @@ -5089,19 +5271,18 @@ void ha_ndbcluster::prepare_for_alter() int ha_ndbcluster::add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys) { - DBUG_ENTER("ha_ndbcluster::add_index"); - DBUG_PRINT("info", ("ha_ndbcluster::add_index to table %s", - table_arg->s->table_name)); int error= 0; uint idx; - + DBUG_ENTER("ha_ndbcluster::add_index"); + DBUG_PRINT("enter", ("table %s", table_arg->s->table_name.str)); DBUG_ASSERT(m_share->state == NSS_ALTERED); + for (idx= 0; idx < num_of_keys; idx++) { KEY *key= key_info + idx; KEY_PART_INFO *key_part= key->key_part; KEY_PART_INFO *end= key_part + key->key_parts; - NDB_INDEX_TYPE idx_type= get_index_type_from_key(idx, key, false); + NDB_INDEX_TYPE idx_type= get_index_type_from_key(idx, key_info, false); DBUG_PRINT("info", ("Adding index: '%s'", key_info[idx].name)); // Add fields to key_part struct for (; key_part != end; key_part++) @@ -5114,6 +5295,9 @@ int ha_ndbcluster::add_index(TABLE *table_arg, if (error) { set_ndb_share_state(m_share, NSS_INITIAL); + /* ndb_share reference schema free */ + DBUG_PRINT("NDB_SHARE", ("%s binlog schema free use_count: %u", + m_share->key, m_share->use_count)); free_share(&m_share); // Decrease ref_count } DBUG_RETURN(error); @@ -5158,6 +5342,9 @@ int ha_ndbcluster::final_drop_index(TABLE *table_arg) if((error= drop_indexes(ndb, table_arg))) { m_share->state= NSS_INITIAL; + /* ndb_share reference schema free */ + DBUG_PRINT("NDB_SHARE", ("%s binlog schema free use_count: %u", + m_share->key, m_share->use_count)); free_share(&m_share); // Decrease ref_count } DBUG_RETURN(error); @@ -5199,10 +5386,13 @@ int ha_ndbcluster::rename_table(const char *from, const char *to) int ndb_table_id= orig_tab->getObjectId(); int ndb_table_version= orig_tab->getObjectVersion(); - NDB_SHARE *share= get_share(from, 0, false); + /* ndb_share reference temporary */ + NDB_SHARE *share= get_share(from, 0, FALSE); if (share) { - int r= rename_share(share, to); + DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u", + share->key, share->use_count)); + IF_DBUG(int r=) rename_share(share, to); DBUG_ASSERT(r == 0); } #endif @@ -5223,8 +5413,11 @@ int ha_ndbcluster::rename_table(const char *from, const char *to) #ifdef HAVE_NDB_BINLOG if (share) { - int r= rename_share(share, from); - DBUG_ASSERT(r == 0); + IF_DBUG(int ret=) rename_share(share, from); + DBUG_ASSERT(ret == 0); + /* ndb_share reference temporary free */ + DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u", + share->key, share->use_count)); free_share(&share); } #endif @@ -5237,7 +5430,12 @@ int ha_ndbcluster::rename_table(const char *from, const char *to) // ToDo in 4.1 should rollback alter table... #ifdef HAVE_NDB_BINLOG if (share) + { + /* ndb_share reference temporary free */ + DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u", + share->key, share->use_count)); free_share(&share); + } #endif DBUG_RETURN(result); } @@ -5271,7 +5469,7 @@ int ha_ndbcluster::rename_table(const char *from, const char *to) if (ndb_extra_logging) sql_print_information("NDB Binlog: RENAME Event: %s", event_name.c_ptr()); - if (share && ndb_binlog_running && + if (share && ndbcluster_create_event_ops(share, ndbtab, event_name.c_ptr())) { sql_print_error("NDB Binlog: FAILED create event operations " @@ -5318,7 +5516,12 @@ int ha_ndbcluster::rename_table(const char *from, const char *to) } } if (share) + { + /* ndb_share reference temporary free */ + DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u", + share->key, share->use_count)); free_share(&share); + } #endif DBUG_RETURN(result); @@ -5348,12 +5551,18 @@ ha_ndbcluster::delete_table(ha_ndbcluster *h, Ndb *ndb, Don't allow drop table unless schema distribution table is setup */ - if (!schema_share) + if (!ndb_schema_share) { DBUG_PRINT("info", ("Schema distribution table not setup")); DBUG_RETURN(HA_ERR_NO_CONNECTION); } - NDB_SHARE *share= get_share(path, 0, false); + /* ndb_share reference temporary */ + NDB_SHARE *share= get_share(path, 0, FALSE); + if (share) + { + DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u", + share->key, share->use_count)); + } #endif /* Drop the table from NDB */ @@ -5433,9 +5642,14 @@ retry_temporary_error1: The share kept by the server has not been freed, free it */ share->state= NSS_DROPPED; + /* ndb_share reference create free */ + DBUG_PRINT("NDB_SHARE", ("%s create free use_count: %u", + share->key, share->use_count)); free_share(&share, TRUE); } - /* free the share taken above */ + /* ndb_share reference temporary free */ + DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u", + share->key, share->use_count)); free_share(&share, TRUE); pthread_mutex_unlock(&ndbcluster_mutex); } @@ -5485,9 +5699,14 @@ retry_temporary_error1: The share kept by the server has not been freed, free it */ share->state= NSS_DROPPED; + /* ndb_share reference create free */ + DBUG_PRINT("NDB_SHARE", ("%s create free use_count: %u", + share->key, share->use_count)); free_share(&share, TRUE); } - /* free the share taken above */ + /* ndb_share reference temporary free */ + DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u", + share->key, share->use_count)); free_share(&share, TRUE); pthread_mutex_unlock(&ndbcluster_mutex); } @@ -5507,7 +5726,7 @@ int ha_ndbcluster::delete_table(const char *name) Don't allow drop table unless schema distribution table is setup */ - if (!schema_share) + if (!ndb_schema_share) { DBUG_PRINT("info", ("Schema distribution table not setup")); DBUG_RETURN(HA_ERR_NO_CONNECTION); @@ -5663,6 +5882,9 @@ ha_ndbcluster::~ha_ndbcluster() if (m_share) { + /* ndb_share reference handler free */ + DBUG_PRINT("NDB_SHARE", ("%s handler free use_count: %u", + m_share->key, m_share->use_count)); free_share(&m_share); } release_metadata(thd, ndb); @@ -5725,14 +5947,21 @@ int ha_ndbcluster::open(const char *name, int mode, uint test_if_locked) DBUG_PRINT("info", ("ref_length: %d", ref_length)); // Init table lock structure + /* ndb_share reference handler */ if (!(m_share=get_share(name, table))) DBUG_RETURN(1); + DBUG_PRINT("NDB_SHARE", ("%s handler use_count: %u", + m_share->key, m_share->use_count)); thr_lock_data_init(&m_share->lock,&m_lock,(void*) 0); set_dbname(name); set_tabname(name); - if (check_ndb_connection()) { + if (check_ndb_connection()) + { + /* ndb_share reference handler free */ + DBUG_PRINT("NDB_SHARE", ("%s handler free use_count: %u", + m_share->key, m_share->use_count)); free_share(&m_share); m_share= 0; DBUG_RETURN(HA_ERR_NO_CONNECTION); @@ -5740,7 +5969,17 @@ int ha_ndbcluster::open(const char *name, int mode, uint test_if_locked) res= get_metadata(name); if (!res) - info(HA_STATUS_VARIABLE | HA_STATUS_CONST); + { + Ndb *ndb= get_ndb(); + ndb->setDatabaseName(m_dbname); + struct Ndb_statistics stat; + res= ndb_get_table_statistics(NULL, FALSE, ndb, m_table, &stat); + stats.mean_rec_length= stat.row_size; + stats.data_file_length= stat.fragment_memory; + stats.records= stat.row_count; + if(!res) + res= info(HA_STATUS_CONST); + } #ifdef HAVE_NDB_BINLOG if (!ndb_binlog_tables_inited && ndb_binlog_running) @@ -5781,8 +6020,11 @@ void ha_ndbcluster::set_part_info(partition_info *part_info) int ha_ndbcluster::close(void) { DBUG_ENTER("close"); - THD *thd= current_thd; + THD *thd= table->in_use; Ndb *ndb= thd ? check_ndb_in_thd(thd) : g_ndb; + /* ndb_share reference handler free */ + DBUG_PRINT("NDB_SHARE", ("%s handler free use_count: %u", + m_share->key, m_share->use_count)); free_share(&m_share); m_share= 0; release_metadata(thd, ndb); @@ -5889,7 +6131,13 @@ int ndbcluster_discover(handlerton *hton, THD* thd, const char *db, ndb->setDatabaseName(db); NDBDICT* dict= ndb->getDictionary(); build_table_filename(key, sizeof(key), db, name, "", 0); - NDB_SHARE *share= get_share(key, 0, false); + /* ndb_share reference temporary */ + NDB_SHARE *share= get_share(key, 0, FALSE); + if (share) + { + DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u", + share->key, share->use_count)); + } if (share && get_ndb_share_state(share) == NSS_ALTERED) { // Frm has been altered on disk, but not yet written to ndb @@ -5935,12 +6183,22 @@ int ndbcluster_discover(handlerton *hton, THD* thd, const char *db, *frmblob= data; if (share) + { + /* ndb_share reference temporary free */ + DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u", + share->key, share->use_count)); free_share(&share); + } DBUG_RETURN(0); err: if (share) + { + /* ndb_share reference temporary free */ + DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u", + share->key, share->use_count)); free_share(&share); + } if (ndb_error.code) { ERR_RETURN(ndb_error); @@ -6056,14 +6314,13 @@ int ndbcluster_drop_database_impl(const char *path) static void ndbcluster_drop_database(handlerton *hton, char *path) { - THD *thd= current_thd; DBUG_ENTER("ndbcluster_drop_database"); #ifdef HAVE_NDB_BINLOG /* Don't allow drop database unless schema distribution table is setup */ - if (!schema_share) + if (!ndb_schema_share) { DBUG_PRINT("info", ("Schema distribution table not setup")); DBUG_VOID_RETURN; @@ -6073,6 +6330,7 @@ static void ndbcluster_drop_database(handlerton *hton, char *path) ndbcluster_drop_database_impl(path); #ifdef HAVE_NDB_BINLOG char db[FN_REFLEN]; + THD *thd= current_thd; ha_ndbcluster::set_dbname(path, db); ndbcluster_log_schema_op(thd, 0, thd->query, thd->query_length, @@ -6080,9 +6338,7 @@ static void ndbcluster_drop_database(handlerton *hton, char *path) #endif DBUG_VOID_RETURN; } -/* - find all tables in ndb and discover those needed -*/ + int ndb_create_table_from_engine(THD *thd, const char *db, const char *table_name) { @@ -6095,18 +6351,22 @@ int ndb_create_table_from_engine(THD *thd, const char *db, return res; } +/* + find all tables in ndb and discover those needed +*/ int ndbcluster_find_all_files(THD *thd) { - DBUG_ENTER("ndbcluster_find_all_files"); Ndb* ndb; char key[FN_REFLEN]; + NDBDICT *dict; + int unhandled, retries= 5, skipped; + DBUG_ENTER("ndbcluster_find_all_files"); if (!(ndb= check_ndb_in_thd(thd))) DBUG_RETURN(HA_ERR_NO_CONNECTION); - NDBDICT *dict= ndb->getDictionary(); + dict= ndb->getDictionary(); - int unhandled, retries= 5, skipped; LINT_INIT(unhandled); LINT_INIT(skipped); do @@ -6176,7 +6436,13 @@ int ndbcluster_find_all_files(THD *thd) } else if (cmp_frm(ndbtab, pack_data, pack_length)) { - NDB_SHARE *share= get_share(key, 0, false); + /* ndb_share reference temporary */ + NDB_SHARE *share= get_share(key, 0, FALSE); + if (share) + { + DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u", + share->key, share->use_count)); + } if (!share || get_ndb_share_state(share) != NSS_ALTERED) { discover= 1; @@ -6184,7 +6450,12 @@ int ndbcluster_find_all_files(THD *thd) elmt.database, elmt.name); } if (share) + { + /* ndb_share reference temporary free */ + DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u", + share->key, share->use_count)); free_share(&share); + } } my_free((char*) data, MYF(MY_ALLOW_ZERO_PTR)); my_free((char*) pack_data, MYF(MY_ALLOW_ZERO_PTR)); @@ -6290,12 +6561,12 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, List<char> delete_list; while ((file_name=it++)) { - bool file_on_disk= false; + bool file_on_disk= FALSE; DBUG_PRINT("info", ("%s", file_name)); if (hash_search(&ndb_tables, file_name, strlen(file_name))) { DBUG_PRINT("info", ("%s existed in NDB _and_ on disk ", file_name)); - file_on_disk= true; + file_on_disk= TRUE; } // Check for .ndb file with this name @@ -6411,6 +6682,23 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, hash_free(&ok_tables); hash_free(&ndb_tables); + + // Delete schema file from files + if (!strcmp(db, NDB_REP_DB)) + { + uint count = 0; + while (count++ < files->elements) + { + file_name = (char *)files->pop(); + if (!strcmp(file_name, NDB_SCHEMA_TABLE)) + { + DBUG_PRINT("info", ("skip %s.%s table, it should be hidden to user", + NDB_REP_DB, NDB_SCHEMA_TABLE)); + continue; + } + files->push_back(file_name); + } + } } // extra bracket to avoid gcc 2.95.3 warning DBUG_RETURN(0); } @@ -6424,6 +6712,7 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, /* Call back after cluster connect */ static int connect_callback() { + pthread_mutex_lock(&LOCK_ndb_util_thread); update_status_variables(g_ndb_cluster_connection); uint node_id, i= 0; @@ -6433,6 +6722,7 @@ static int connect_callback() g_node_id_map[node_id]= i++; pthread_cond_signal(&COND_ndb_util_thread); + pthread_mutex_unlock(&LOCK_ndb_util_thread); return 0; } @@ -6443,6 +6733,15 @@ static int ndbcluster_init(void *p) int res; DBUG_ENTER("ndbcluster_init"); + if (ndbcluster_inited) + DBUG_RETURN(FALSE); + + pthread_mutex_init(&ndbcluster_mutex,MY_MUTEX_INIT_FAST); + pthread_mutex_init(&LOCK_ndb_util_thread, MY_MUTEX_INIT_FAST); + pthread_cond_init(&COND_ndb_util_thread, NULL); + pthread_cond_init(&COND_ndb_util_ready, NULL); + ndb_util_thread_running= -1; + ndbcluster_terminating= 0; ndb_dictionary_is_mysqld= 1; ndbcluster_hton= (handlerton *)p; @@ -6488,7 +6787,7 @@ static int ndbcluster_init(void *p) } { char buf[128]; - my_snprintf(buf, sizeof(buf), "mysqld --server-id=%d", server_id); + my_snprintf(buf, sizeof(buf), "mysqld --server-id=%lu", server_id); g_ndb_cluster_connection->set_name(buf); } g_ndb_cluster_connection->set_optimized_node_selection @@ -6541,17 +6840,12 @@ static int ndbcluster_init(void *p) (void) hash_init(&ndbcluster_open_tables,system_charset_info,32,0,0, (hash_get_key) ndbcluster_get_key,0,0); - pthread_mutex_init(&ndbcluster_mutex,MY_MUTEX_INIT_FAST); #ifdef HAVE_NDB_BINLOG /* start the ndb injector thread */ if (ndbcluster_binlog_start()) goto ndbcluster_init_error; #endif /* HAVE_NDB_BINLOG */ - pthread_mutex_init(&LOCK_ndb_util_thread, MY_MUTEX_INIT_FAST); - pthread_cond_init(&COND_ndb_util_thread, NULL); - - ndb_cache_check_time = opt_ndb_cache_check_time; // Create utility thread pthread_t tmp; @@ -6562,6 +6856,24 @@ static int ndbcluster_init(void *p) pthread_mutex_destroy(&ndbcluster_mutex); pthread_mutex_destroy(&LOCK_ndb_util_thread); pthread_cond_destroy(&COND_ndb_util_thread); + pthread_cond_destroy(&COND_ndb_util_ready); + goto ndbcluster_init_error; + } + + /* Wait for the util thread to start */ + pthread_mutex_lock(&LOCK_ndb_util_thread); + while (ndb_util_thread_running < 0) + pthread_cond_wait(&COND_ndb_util_ready, &LOCK_ndb_util_thread); + pthread_mutex_unlock(&LOCK_ndb_util_thread); + + if (!ndb_util_thread_running) + { + DBUG_PRINT("error", ("ndb utility thread exited prematurely")); + hash_free(&ndbcluster_open_tables); + pthread_mutex_destroy(&ndbcluster_mutex); + pthread_mutex_destroy(&LOCK_ndb_util_thread); + pthread_cond_destroy(&COND_ndb_util_thread); + pthread_cond_destroy(&COND_ndb_util_ready); goto ndbcluster_init_error; } @@ -6587,6 +6899,17 @@ static int ndbcluster_end(handlerton *hton, ha_panic_function type) if (!ndbcluster_inited) DBUG_RETURN(0); + ndbcluster_inited= 0; + + /* wait for util thread to finish */ + sql_print_information("Stopping Cluster Utility thread"); + pthread_mutex_lock(&LOCK_ndb_util_thread); + ndbcluster_terminating= 1; + pthread_cond_signal(&COND_ndb_util_thread); + while (ndb_util_thread_running > 0) + pthread_cond_wait(&COND_ndb_util_ready, &LOCK_ndb_util_thread); + pthread_mutex_unlock(&LOCK_ndb_util_thread); + #ifdef HAVE_NDB_BINLOG { @@ -6599,7 +6922,7 @@ static int ndbcluster_end(handlerton *hton, ha_panic_function type) fprintf(stderr, "NDB: table share %s with use_count %d not freed\n", share->key, share->use_count); #endif - real_free_share(&share); + ndbcluster_real_free_share(&share); } pthread_mutex_unlock(&ndbcluster_mutex); } @@ -6633,14 +6956,14 @@ static int ndbcluster_end(handlerton *hton, ha_panic_function type) pthread_mutex_destroy(&ndbcluster_mutex); pthread_mutex_destroy(&LOCK_ndb_util_thread); pthread_cond_destroy(&COND_ndb_util_thread); - ndbcluster_inited= 0; + pthread_cond_destroy(&COND_ndb_util_ready); DBUG_RETURN(0); } void ha_ndbcluster::print_error(int error, myf errflag) { DBUG_ENTER("ha_ndbcluster::print_error"); - DBUG_PRINT("enter", ("error = %d", error)); + DBUG_PRINT("enter", ("error: %d", error)); if (error == HA_ERR_NO_PARTITION_FOUND) m_part_info->print_no_partition_found(table); @@ -6796,19 +7119,19 @@ ha_ndbcluster::records_in_range(uint inx, key_range *min_key, { // We must provide approx table rows Uint64 table_rows=0; - Ndb_local_table_statistics *info= m_table_info; - if (info->records != ~(ha_rows)0 && info->records != 0) + Ndb_local_table_statistics *ndb_info= m_table_info; + if (ndb_info->records != ~(ha_rows)0 && ndb_info->records != 0) { - table_rows = info->records; - DBUG_PRINT("info", ("use info->records: %llu", table_rows)); + table_rows = ndb_info->records; + DBUG_PRINT("info", ("use info->records: %lu", (ulong) table_rows)); } else { Ndb_statistics stat; - if ((res=ndb_get_table_statistics(ndb, m_table, &stat)) != 0) + if ((res=ndb_get_table_statistics(this, TRUE, ndb, m_table, &stat))) break; table_rows=stat.row_count; - DBUG_PRINT("info", ("use db row_count: %llu", table_rows)); + DBUG_PRINT("info", ("use db row_count: %lu", (ulong) table_rows)); if (table_rows == 0) { // Problem if autocommit=0 #ifdef ndb_get_table_statistics_uses_active_trans @@ -6831,7 +7154,7 @@ ha_ndbcluster::records_in_range(uint inx, key_range *min_key, if ((op->readTuples(NdbOperation::LM_CommittedRead)) == -1) ERR_BREAK(op->getNdbError(), res); const key_range *keys[2]={ min_key, max_key }; - if ((res=set_bounds(op, inx, true, keys)) != 0) + if ((res=set_bounds(op, inx, TRUE, keys)) != 0) break; // Decide if db should be contacted @@ -6937,7 +7260,10 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname, DBUG_PRINT("info", ("Table %s not found in ndbcluster_open_tables", name)); DBUG_RETURN(1); } + /* ndb_share reference temporary, free below */ share->use_count++; + DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u", + share->key, share->use_count)); pthread_mutex_unlock(&ndbcluster_mutex); pthread_mutex_lock(&share->mutex); @@ -6946,10 +7272,15 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname, if (share->commit_count != 0) { *commit_count= share->commit_count; +#ifndef DBUG_OFF char buff[22]; +#endif DBUG_PRINT("info", ("Getting commit_count: %s from share", llstr(share->commit_count, buff))); pthread_mutex_unlock(&share->mutex); + /* ndb_share reference temporary free */ + DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u", + share->key, share->use_count)); free_share(&share); DBUG_RETURN(0); } @@ -6966,8 +7297,11 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname, { Ndb_table_guard ndbtab_g(ndb->getDictionary(), tabname); if (ndbtab_g.get_table() == 0 - || ndb_get_table_statistics(ndb, ndbtab_g.get_table(), &stat)) + || ndb_get_table_statistics(NULL, FALSE, ndb, ndbtab_g.get_table(), &stat)) { + /* ndb_share reference temporary free */ + DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u", + share->key, share->use_count)); free_share(&share); DBUG_RETURN(1); } @@ -6976,7 +7310,9 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname, pthread_mutex_lock(&share->mutex); if (share->commit_count_lock == lock) { +#ifndef DBUG_OFF char buff[22]; +#endif DBUG_PRINT("info", ("Setting commit_count to %s", llstr(stat.commit_count, buff))); share->commit_count= stat.commit_count; @@ -6988,6 +7324,9 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname, *commit_count= 0; } pthread_mutex_unlock(&share->mutex); + /* ndb_share reference temporary free */ + DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u", + share->key, share->use_count)); free_share(&share); DBUG_RETURN(0); } @@ -7032,7 +7371,9 @@ ndbcluster_cache_retrieval_allowed(THD *thd, bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); char *dbname= full_name; char *tabname= dbname+strlen(dbname)+1; +#ifndef DBUG_OFF char buff[22], buff2[22]; +#endif DBUG_ENTER("ndbcluster_cache_retrieval_allowed"); DBUG_PRINT("enter", ("dbname: %s, tabname: %s, is_autocommit: %d", dbname, tabname, is_autocommit)); @@ -7099,7 +7440,9 @@ ha_ndbcluster::register_query_cache_table(THD *thd, ulonglong *engine_data) { Uint64 commit_count; +#ifndef DBUG_OFF char buff[22]; +#endif bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); DBUG_ENTER("ha_ndbcluster::register_query_cache_table"); DBUG_PRINT("enter",("dbname: %s, tabname: %s, is_autocommit: %d", @@ -7139,33 +7482,54 @@ static byte *ndbcluster_get_key(NDB_SHARE *share,uint *length, return (byte*) share->key; } + #ifndef DBUG_OFF -static void dbug_print_open_tables() + +static void print_share(const char* where, NDB_SHARE* share) { - DBUG_ENTER("dbug_print_open_tables"); - for (uint i= 0; i < ndbcluster_open_tables.records; i++) - { - NDB_SHARE *share= (NDB_SHARE*) hash_element(&ndbcluster_open_tables, i); - DBUG_PRINT("share", - ("[%d] 0x%lx key: %s key_length: %d", - i, share, share->key, share->key_length)); - DBUG_PRINT("share", - ("db.tablename: %s.%s use_count: %d commit_count: %d", - share->db, share->table_name, - share->use_count, share->commit_count)); + fprintf(DBUG_FILE, + "%s %s.%s: use_count: %u, commit_count: %lu\n", + where, share->db, share->table_name, share->use_count, + (ulong) share->commit_count); + fprintf(DBUG_FILE, + " - key: %s, key_length: %d\n", + share->key, share->key_length); + #ifdef HAVE_NDB_BINLOG - if (share->table) - DBUG_PRINT("share", - ("table->s->db.table_name: %s.%s", - share->table->s->db.str, share->table->s->table_name.str)); + if (share->table) + fprintf(DBUG_FILE, + " - share->table: %p %s.%s\n", + share->table, share->table->s->db.str, + share->table->s->table_name.str); #endif - } - DBUG_VOID_RETURN; } -#else -#define dbug_print_open_tables() + + +static void print_ndbcluster_open_tables() +{ + DBUG_LOCK_FILE; + fprintf(DBUG_FILE, ">ndbcluster_open_tables\n"); + for (uint i= 0; i < ndbcluster_open_tables.records; i++) + print_share("", + (NDB_SHARE*)hash_element(&ndbcluster_open_tables, i)); + fprintf(DBUG_FILE, "<ndbcluster_open_tables\n"); + DBUG_UNLOCK_FILE; +} + #endif + +#define dbug_print_open_tables() \ + DBUG_EXECUTE("info", \ + print_ndbcluster_open_tables();); + +#define dbug_print_share(t, s) \ + DBUG_LOCK_FILE; \ + DBUG_EXECUTE("info", \ + print_share((t), (s));); \ + DBUG_UNLOCK_FILE; + + #ifdef HAVE_NDB_BINLOG /* For some reason a share is still around, try to salvage the situation @@ -7183,21 +7547,34 @@ int handle_trailing_share(NDB_SHARE *share) static ulong trailing_share_id= 0; DBUG_ENTER("handle_trailing_share"); + /* ndb_share reference temporary, free below */ ++share->use_count; + DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u", + share->key, share->use_count)); pthread_mutex_unlock(&ndbcluster_mutex); TABLE_LIST table_list; bzero((char*) &table_list,sizeof(table_list)); table_list.db= share->db; table_list.alias= table_list.table_name= share->table_name; + safe_mutex_assert_owner(&LOCK_open); close_cached_tables(thd, 0, &table_list, TRUE); pthread_mutex_lock(&ndbcluster_mutex); + /* ndb_share reference temporary free */ + DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u", + share->key, share->use_count)); if (!--share->use_count) { - DBUG_PRINT("info", ("NDB_SHARE: close_cashed_tables %s freed share.", - share->key)); - real_free_share(&share); + if (ndb_extra_logging) + sql_print_information("NDB_SHARE: trailing share " + "%s(connect_count: %u) " + "released by close_cached_tables at " + "connect_count: %u", + share->key, + share->connect_count, + g_ndb_cluster_connection->get_connect_count()); + ndbcluster_real_free_share(&share); DBUG_RETURN(0); } @@ -7205,16 +7582,28 @@ int handle_trailing_share(NDB_SHARE *share) share still exists, if share has not been dropped by server release that share */ - if (share->state != NSS_DROPPED && !--share->use_count) + if (share->state != NSS_DROPPED) { - DBUG_PRINT("info", ("NDB_SHARE: %s already exists, " - "use_count=%d state != NSS_DROPPED.", - share->key, share->use_count)); - real_free_share(&share); - DBUG_RETURN(0); + share->state= NSS_DROPPED; + /* ndb_share reference create free */ + DBUG_PRINT("NDB_SHARE", ("%s create free use_count: %u", + share->key, share->use_count)); + --share->use_count; + + if (share->use_count == 0) + { + if (ndb_extra_logging) + sql_print_information("NDB_SHARE: trailing share " + "%s(connect_count: %u) " + "released after NSS_DROPPED check " + "at connect_count: %u", + share->key, + share->connect_count, + g_ndb_cluster_connection->get_connect_count()); + ndbcluster_real_free_share(&share); + DBUG_RETURN(0); + } } - DBUG_PRINT("error", ("NDB_SHARE: %s already exists use_count=%d.", - share->key, share->use_count)); sql_print_error("NDB_SHARE: %s already exists use_count=%d." " Moving away for safety, but possible memleak.", @@ -7245,7 +7634,7 @@ int handle_trailing_share(NDB_SHARE *share) share->key_length= min_key_length; } share->key_length= - my_snprintf(share->key, min_key_length + 1, "#leak%d", + my_snprintf(share->key, min_key_length + 1, "#leak%lu", trailing_share_id++); } /* Keep it for possible the future trailing free */ @@ -7308,19 +7697,9 @@ static int rename_share(NDB_SHARE *share, const char *new_key) share->table_name= share->db + strlen(share->db) + 1; ha_ndbcluster::set_tabname(new_key, share->table_name); - DBUG_PRINT("rename_share", - ("0x%lx key: %s key_length: %d", - share, share->key, share->key_length)); - DBUG_PRINT("rename_share", - ("db.tablename: %s.%s use_count: %d commit_count: %d", - share->db, share->table_name, - share->use_count, share->commit_count)); + dbug_print_share("rename_share:", share); if (share->table) { - DBUG_PRINT("rename_share", - ("table->s->db.table_name: %s.%s", - share->table->s->db.str, share->table->s->table_name.str)); - if (share->op == 0) { share->table->s->db.str= share->db; @@ -7348,14 +7727,7 @@ NDB_SHARE *ndbcluster_get_share(NDB_SHARE *share) share->use_count++; dbug_print_open_tables(); - - DBUG_PRINT("get_share", - ("0x%lx key: %s key_length: %d", - share, share->key, share->key_length)); - DBUG_PRINT("get_share", - ("db.tablename: %s.%s use_count: %d commit_count: %d", - share->db, share->table_name, - share->use_count, share->commit_count)); + dbug_print_share("ndbcluster_get_share:", share); pthread_mutex_unlock(&ndbcluster_mutex); return share; } @@ -7380,7 +7752,6 @@ NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table, bool create_if_not_exists, bool have_lock) { - THD *thd= current_thd; NDB_SHARE *share; uint length= (uint) strlen(key); DBUG_ENTER("ndbcluster_get_share"); @@ -7446,14 +7817,7 @@ NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table, share->use_count++; dbug_print_open_tables(); - - DBUG_PRINT("info", - ("0x%lx key: %s key_length: %d key: %s", - share, share->key, share->key_length, key)); - DBUG_PRINT("info", - ("db.tablename: %s.%s use_count: %d commit_count: %d", - share->db, share->table_name, - share->use_count, share->commit_count)); + dbug_print_share("ndbcluster_get_share:", share); if (!have_lock) pthread_mutex_unlock(&ndbcluster_mutex); DBUG_RETURN(share); @@ -7463,13 +7827,7 @@ NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table, void ndbcluster_real_free_share(NDB_SHARE **share) { DBUG_ENTER("ndbcluster_real_free_share"); - DBUG_PRINT("real_free_share", - ("0x%lx key: %s key_length: %d", - (*share), (*share)->key, (*share)->key_length)); - DBUG_PRINT("real_free_share", - ("db.tablename: %s.%s use_count: %d commit_count: %d", - (*share)->db, (*share)->table_name, - (*share)->use_count, (*share)->commit_count)); + dbug_print_share("ndbcluster_real_free_share:", *share); hash_delete(&ndbcluster_open_tables, (byte*) *share); thr_lock_delete(&(*share)->lock); @@ -7498,12 +7856,7 @@ void ndbcluster_real_free_share(NDB_SHARE **share) DBUG_VOID_RETURN; } -/* - decrease refcount of share - calls real_free_share when refcount reaches 0 - have_lock == TRUE, pthread_mutex_lock(&ndbcluster_mutex) already taken -*/ void ndbcluster_free_share(NDB_SHARE **share, bool have_lock) { if (!have_lock) @@ -7512,18 +7865,12 @@ void ndbcluster_free_share(NDB_SHARE **share, bool have_lock) (*share)->util_lock= 0; if (!--(*share)->use_count) { - real_free_share(share); + ndbcluster_real_free_share(share); } else { dbug_print_open_tables(); - DBUG_PRINT("free_share", - ("0x%lx key: %s key_length: %d", - *share, (*share)->key, (*share)->key_length)); - DBUG_PRINT("free_share", - ("db.tablename: %s.%s use_count: %d commit_count: %d", - (*share)->db, (*share)->table_name, - (*share)->use_count, (*share)->commit_count)); + dbug_print_share("ndbcluster_free_share:", *share); } if (!have_lock) pthread_mutex_unlock(&ndbcluster_mutex); @@ -7532,14 +7879,17 @@ void ndbcluster_free_share(NDB_SHARE **share, bool have_lock) static int -ndb_get_table_statistics(Ndb* ndb, const NDBTAB *ndbtab, +ndb_get_table_statistics(ha_ndbcluster* file, bool report_error, Ndb* ndb, const NDBTAB *ndbtab, struct Ndb_statistics * ndbstat) { NdbTransaction* pTrans; NdbError error; int retries= 10; + int reterr= 0; int retry_sleep= 30 * 1000; /* 30 milliseconds */ +#ifndef DBUG_OFF char buff[22], buff2[22], buff3[22], buff4[22]; +#endif DBUG_ENTER("ndb_get_table_statistics"); DBUG_PRINT("enter", ("table: %s", ndbtab->getName())); @@ -7555,7 +7905,6 @@ ndb_get_table_statistics(Ndb* ndb, const NDBTAB *ndbtab, Uint64 sum_row_size= 0; Uint64 sum_mem= 0; NdbScanOperation*pOp; - NdbResultSet *rs; int check; if ((pTrans= ndb->startTransaction()) == NULL) @@ -7591,7 +7940,7 @@ ndb_get_table_statistics(Ndb* ndb, const NDBTAB *ndbtab, (char*)&var_mem); if (pTrans->execute(NdbTransaction::NoCommit, - NdbTransaction::AbortOnError, + NdbOperation::AbortOnError, TRUE) == -1) { error= pTrans->getNdbError(); @@ -7633,6 +7982,22 @@ ndb_get_table_statistics(Ndb* ndb, const NDBTAB *ndbtab, DBUG_RETURN(0); retry: + if(report_error) + { + if (file && pTrans) + { + reterr= file->ndb_err(pTrans); + } + else + { + const NdbError& tmp= error; + ERR_PRINT(tmp); + reterr= ndb_to_mysql_error(&tmp); + } + } + else + reterr= error.code; + if (pTrans) { ndb->closeTransaction(pTrans); @@ -7645,8 +8010,9 @@ retry: } break; } while(1); - DBUG_PRINT("exit", ("failed, error %u(%s)", error.code, error.message)); - ERR_RETURN(error); + DBUG_PRINT("exit", ("failed, reterr: %u, NdbError %u(%s)", reterr, + error.code, error.message)); + DBUG_RETURN(reterr); } /* @@ -7699,6 +8065,30 @@ ha_ndbcluster::release_completed_operations(NdbTransaction *trans, trans->releaseCompletedOperations(); } +bool +ha_ndbcluster::null_value_index_search(KEY_MULTI_RANGE *ranges, + KEY_MULTI_RANGE *end_range, + HANDLER_BUFFER *buffer) +{ + DBUG_ENTER("null_value_index_search"); + KEY* key_info= table->key_info + active_index; + KEY_MULTI_RANGE *range= ranges; + ulong reclength= table->s->reclength; + byte *curr= (byte*)buffer->buffer; + byte *end_of_buffer= (byte*)buffer->buffer_end; + + for (; range<end_range && curr+reclength <= end_of_buffer; + range++) + { + const byte *key= range->start_key.key; + uint key_len= range->start_key.length; + if (check_null_in_key(key_info, key, key_len)) + DBUG_RETURN(TRUE); + curr += reclength; + } + DBUG_RETURN(FALSE); +} + int ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, KEY_MULTI_RANGE *ranges, @@ -7706,21 +8096,23 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, bool sorted, HANDLER_BUFFER *buffer) { - DBUG_ENTER("ha_ndbcluster::read_multi_range_first"); m_write_op= FALSE; - int res; KEY* key_info= table->key_info + active_index; - NDB_INDEX_TYPE index_type= get_index_type(active_index); + NDB_INDEX_TYPE cur_index_type= get_index_type(active_index); ulong reclength= table_share->reclength; NdbOperation* op; Thd_ndb *thd_ndb= get_thd_ndb(current_thd); + DBUG_ENTER("ha_ndbcluster::read_multi_range_first"); - if (uses_blob_value()) + /** + * blobs and unique hash index with NULL can't be batched currently + */ + if (uses_blob_value() || + (cur_index_type == UNIQUE_INDEX && + has_null_in_unique_index(active_index) && + null_value_index_search(ranges, ranges+range_count, buffer))) { - /** - * blobs can't be batched currently - */ m_disable_multi_read= TRUE; DBUG_RETURN(handler::read_multi_range_first(found_range_p, ranges, @@ -7775,7 +8167,7 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, get_partition_set(table, curr, active_index, &multi_range_curr->start_key, &part_spec); - DBUG_PRINT("info", ("part_spec.start_part = %u, part_spec.end_part = %u", + DBUG_PRINT("info", ("part_spec.start_part: %u part_spec.end_part: %u", part_spec.start_part, part_spec.end_part)); /* If partition pruning has found no partition in set @@ -7792,7 +8184,7 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, continue; } } - switch(index_type){ + switch (cur_index_type) { case PRIMARY_KEY_ORDERED_INDEX: if (!(multi_range_curr->start_key.length == key_info->key_length && multi_range_curr->start_key.flag == HA_READ_KEY_EXACT)) @@ -7805,9 +8197,8 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, !op->readTuple(lm) && !set_primary_key(op, multi_range_curr->start_key.key) && !define_read_attrs(curr, op) && - (op->setAbortOption(AO_IgnoreError), TRUE) && (!m_use_partition_function || - (op->setPartitionId(part_spec.start_part), true))) + (op->setPartitionId(part_spec.start_part), TRUE))) curr += reclength; else ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError()); @@ -7827,8 +8218,7 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, if ((op= m_active_trans->getNdbIndexOperation(unique_idx, tab)) && !op->readTuple(lm) && !set_index_key(op, key_info, multi_range_curr->start_key.key) && - !define_read_attrs(curr, op) && - (op->setAbortOption(AO_IgnoreError), TRUE)) + !define_read_attrs(curr, op)) curr += reclength; else ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError()); @@ -7852,7 +8242,7 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, } else if ((scanOp= m_active_trans->getNdbIndexScanOperation(idx, tab)) &&!scanOp->readTuples(lm, 0, parallelism, sorted, - FALSE, TRUE, need_pk) + FALSE, TRUE, need_pk, TRUE) &&!generate_scan_filter(m_cond_stack, scanOp) &&!define_read_attrs(end_of_buffer-reclength, scanOp)) { @@ -7868,7 +8258,7 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, const key_range *keys[2]= { &multi_range_curr->start_key, &multi_range_curr->end_key }; - if ((res= set_bounds(scanOp, active_index, false, keys, + if ((res= set_bounds(scanOp, active_index, FALSE, keys, multi_range_curr-ranges))) DBUG_RETURN(res); break; @@ -7990,7 +8380,7 @@ ha_ndbcluster::read_multi_range_next(KEY_MULTI_RANGE ** multi_range_found_p) DBUG_MULTI_RANGE(6); // First fetch from cursor DBUG_ASSERT(range_no == -1); - if ((res= m_multi_cursor->nextResult(true))) + if ((res= m_multi_cursor->nextResult(TRUE))) { DBUG_MULTI_RANGE(15); goto close_scan; @@ -8028,6 +8418,8 @@ close_scan: if (multi_range_curr == multi_range_end) { DBUG_MULTI_RANGE(16); + Thd_ndb *thd_ndb= get_thd_ndb(current_thd); + thd_ndb->query_state&= NDB_QUERY_NORMAL; DBUG_RETURN(HA_ERR_END_OF_FILE); } @@ -8112,7 +8504,6 @@ ha_ndbcluster::update_table_comment( } ndb->setDatabaseName(m_dbname); - NDBDICT* dict= ndb->getDictionary(); const NDBTAB* tab= m_table; DBUG_ASSERT(tab != NULL); @@ -8135,32 +8526,27 @@ ha_ndbcluster::update_table_comment( pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused))) { THD *thd; /* needs to be first for thread_stack */ - Ndb* ndb; struct timespec abstime; List<NDB_SHARE> util_open_tables; + Thd_ndb *thd_ndb; my_thread_init(); DBUG_ENTER("ndb_util_thread"); - DBUG_PRINT("enter", ("ndb_cache_check_time: %d", ndb_cache_check_time)); + DBUG_PRINT("enter", ("ndb_cache_check_time: %lu", ndb_cache_check_time)); + + pthread_mutex_lock(&LOCK_ndb_util_thread); thd= new THD; /* note that contructor of THD uses DBUG_ */ THD_CHECK_SENTRY(thd); - ndb= new Ndb(g_ndb_cluster_connection, ""); pthread_detach_this_thread(); ndb_util_thread= pthread_self(); thd->thread_stack= (char*)&thd; /* remember where our stack is */ - if (thd->store_globals() || (ndb->init() != 0)) - { - thd->cleanup(); - delete thd; - delete ndb; - DBUG_RETURN(NULL); - } + if (thd->store_globals()) + goto ndb_util_thread_fail; thd->init_for_queries(); thd->version=refresh_version; - thd->set_time(); thd->main_security_ctx.host_or_ip= ""; thd->client_capabilities = 0; my_net_init(&thd->net, 0); @@ -8168,16 +8554,29 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused))) thd->main_security_ctx.priv_user = 0; thd->current_stmt_binlog_row_based= TRUE; // If in mixed mode + /* Signal successful initialization */ + ndb_util_thread_running= 1; + pthread_cond_signal(&COND_ndb_util_ready); + pthread_mutex_unlock(&LOCK_ndb_util_thread); + /* wait for mysql server to start */ pthread_mutex_lock(&LOCK_server_started); while (!mysqld_server_started) - pthread_cond_wait(&COND_server_started, &LOCK_server_started); + { + set_timespec(abstime, 1); + pthread_cond_timedwait(&COND_server_started, &LOCK_server_started, + &abstime); + if (ndbcluster_terminating) + { + pthread_mutex_unlock(&LOCK_server_started); + pthread_mutex_lock(&LOCK_ndb_util_thread); + goto ndb_util_thread_end; + } + } pthread_mutex_unlock(&LOCK_server_started); - ndbcluster_util_inited= 1; - /* Wait for cluster to start */ @@ -8185,28 +8584,21 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused))) while (!ndb_cluster_node_id && (ndbcluster_hton->slot != ~(uint)0)) { /* ndb not connected yet */ - set_timespec(abstime, 1); - pthread_cond_timedwait(&COND_ndb_util_thread, - &LOCK_ndb_util_thread, - &abstime); - if (abort_loop) - { - pthread_mutex_unlock(&LOCK_ndb_util_thread); + pthread_cond_wait(&COND_ndb_util_thread, &LOCK_ndb_util_thread); + if (ndbcluster_terminating) goto ndb_util_thread_end; - } } pthread_mutex_unlock(&LOCK_ndb_util_thread); + /* Get thd_ndb for this thread */ + if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb())) { - Thd_ndb *thd_ndb; - if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb())) - { - sql_print_error("Could not allocate Thd_ndb object"); - goto ndb_util_thread_end; - } - set_thd_ndb(thd, thd_ndb); - thd_ndb->options|= TNO_NO_LOG_SCHEMA_OP; + sql_print_error("Could not allocate Thd_ndb object"); + pthread_mutex_lock(&LOCK_ndb_util_thread); + goto ndb_util_thread_end; } + set_thd_ndb(thd, thd_ndb); + thd_ndb->options|= TNO_NO_LOG_SCHEMA_OP; #ifdef HAVE_NDB_BINLOG if (ndb_extra_logging && ndb_binlog_running) @@ -8221,23 +8613,25 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused))) #endif set_timespec(abstime, 0); - for (;!abort_loop;) + for (;;) { pthread_mutex_lock(&LOCK_ndb_util_thread); - pthread_cond_timedwait(&COND_ndb_util_thread, - &LOCK_ndb_util_thread, - &abstime); + if (!ndbcluster_terminating) + pthread_cond_timedwait(&COND_ndb_util_thread, + &LOCK_ndb_util_thread, + &abstime); + if (ndbcluster_terminating) /* Shutting down server */ + goto ndb_util_thread_end; pthread_mutex_unlock(&LOCK_ndb_util_thread); #ifdef NDB_EXTRA_DEBUG_UTIL_THREAD - DBUG_PRINT("ndb_util_thread", ("Started, ndb_cache_check_time: %d", + DBUG_PRINT("ndb_util_thread", ("Started, ndb_cache_check_time: %lu", ndb_cache_check_time)); #endif - if (abort_loop) - break; /* Shutting down server */ #ifdef HAVE_NDB_BINLOG /* - Check that the apply_status_share and schema_share has been created. + Check that the ndb_apply_status_share and ndb_schema_share + have been created. If not try to create it */ if (!ndb_binlog_tables_inited) @@ -8263,7 +8657,10 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused))) continue; // injector thread is the only user, skip statistics share->util_lock= current_thd; // Mark that util thread has lock #endif /* HAVE_NDB_BINLOG */ + /* ndb_share reference temporary, free below */ share->use_count++; /* Make sure the table can't be closed */ + DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u", + share->key, share->use_count)); DBUG_PRINT("ndb_util_thread", ("Found open table[%d]: %s, use_count: %d", i, share->table_name, share->use_count)); @@ -8284,31 +8681,36 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused))) /* Util thread and injector thread is the only user, skip statistics */ + /* ndb_share reference temporary free */ + DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u", + share->key, share->use_count)); free_share(&share); continue; } #endif /* HAVE_NDB_BINLOG */ DBUG_PRINT("ndb_util_thread", - ("Fetching commit count for: %s", - share->key)); + ("Fetching commit count for: %s", share->key)); - /* Contact NDB to get commit count for table */ - ndb->setDatabaseName(share->db); struct Ndb_statistics stat; - uint lock; pthread_mutex_lock(&share->mutex); lock= share->commit_count_lock; pthread_mutex_unlock(&share->mutex); { + /* Contact NDB to get commit count for table */ + Ndb* ndb= thd_ndb->ndb; + ndb->setDatabaseName(share->db); Ndb_table_guard ndbtab_g(ndb->getDictionary(), share->table_name); if (ndbtab_g.get_table() && - ndb_get_table_statistics(ndb, ndbtab_g.get_table(), &stat) == 0) + ndb_get_table_statistics(NULL, FALSE, ndb, + ndbtab_g.get_table(), &stat) == 0) { +#ifndef DBUG_OFF char buff[22], buff2[22]; - DBUG_PRINT("ndb_util_thread", - ("Table: %s, commit_count: %llu, rows: %llu", +#endif + DBUG_PRINT("info", + ("Table: %s commit_count: %s rows: %s", share->key, llstr(stat.commit_count, buff), llstr(stat.row_count, buff2))); @@ -8327,7 +8729,9 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused))) share->commit_count= stat.commit_count; pthread_mutex_unlock(&share->mutex); - /* Decrease the use count and possibly free share */ + /* ndb_share reference temporary free */ + DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u", + share->key, share->use_count)); free_share(&share); } @@ -8355,12 +8759,19 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused))) abstime.tv_nsec-= 1000000000; } } + + pthread_mutex_lock(&LOCK_ndb_util_thread); + ndb_util_thread_end: - sql_print_information("Stopping Cluster Utility thread"); net_end(&thd->net); +ndb_util_thread_fail: thd->cleanup(); delete thd; - delete ndb; + + /* signal termination */ + ndb_util_thread_running= 0; + pthread_cond_signal(&COND_ndb_util_ready); + pthread_mutex_unlock(&LOCK_ndb_util_thread); DBUG_PRINT("exit", ("ndb_util_thread")); my_thread_end(); pthread_exit(0); @@ -8477,14 +8888,14 @@ void ndb_serialize_cond(const Item *item, void *arg) if (context->supported) { - Ndb_rewrite_context *rewrite_context= context->rewrite_stack; - const Item_func *func_item; + Ndb_rewrite_context *rewrite_context2= context->rewrite_stack; + const Item_func *rewrite_func_item; // Check if we are rewriting some unsupported function call - if (rewrite_context && - (func_item= rewrite_context->func_item) && - rewrite_context->count++ == 0) + if (rewrite_context2 && + (rewrite_func_item= rewrite_context2->func_item) && + rewrite_context2->count++ == 0) { - switch (func_item->functype()) { + switch (rewrite_func_item->functype()) { case Item_func::BETWEEN: /* Rewrite @@ -8511,7 +8922,7 @@ void ndb_serialize_cond(const Item *item, void *arg) if (context->expecting(item->type())) { // This is the <field>|<const> item, save it in the rewrite context - rewrite_context->left_hand_item= item; + rewrite_context2->left_hand_item= item; if (item->type() == Item::FUNC_ITEM) { Item_func *func_item= (Item_func *) item; @@ -8655,7 +9066,7 @@ void ndb_serialize_cond(const Item *item, void *arg) Check that the field is part of the table of the handler instance and that we expect a field with of this result type. */ - if (context->table == field->table) + if (context->table->s == field->table->s) { const NDBTAB *tab= (const NDBTAB *) context->ndb_table; DBUG_PRINT("info", ("FIELD_ITEM")); @@ -8676,7 +9087,7 @@ void ndb_serialize_cond(const Item *item, void *arg) type == MYSQL_TYPE_DATETIME) ? (context->expecting_field_result(STRING_RESULT) || context->expecting_field_result(INT_RESULT)) - : true)) && + : TRUE)) && // Bit fields no yet supported in scan filter type != MYSQL_TYPE_BIT && // No BLOB support in scan filter @@ -9149,8 +9560,8 @@ void ndb_serialize_cond(const Item *item, void *arg) DBUG_PRINT("info", ("INT_ITEM")); if (context->expecting(Item::INT_ITEM)) { - Item_int *int_item= (Item_int *) item; - DBUG_PRINT("info", ("value %d", int_item->value)); + DBUG_PRINT("info", ("value %ld", + (long) ((Item_int*) item)->value)); NDB_ITEM_QUALIFICATION q; q.value_type= Item::INT_ITEM; curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item); @@ -9173,11 +9584,10 @@ void ndb_serialize_cond(const Item *item, void *arg) context->supported= FALSE; break; case Item::REAL_ITEM: - DBUG_PRINT("info", ("REAL_ITEM %s")); + DBUG_PRINT("info", ("REAL_ITEM")); if (context->expecting(Item::REAL_ITEM)) { - Item_float *float_item= (Item_float *) item; - DBUG_PRINT("info", ("value %f", float_item->value)); + DBUG_PRINT("info", ("value %f", ((Item_float*) item)->value)); NDB_ITEM_QUALIFICATION q; q.value_type= Item::REAL_ITEM; curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item); @@ -9221,11 +9631,11 @@ void ndb_serialize_cond(const Item *item, void *arg) context->supported= FALSE; break; case Item::DECIMAL_ITEM: - DBUG_PRINT("info", ("DECIMAL_ITEM %s")); + DBUG_PRINT("info", ("DECIMAL_ITEM")); if (context->expecting(Item::DECIMAL_ITEM)) { - Item_decimal *decimal_item= (Item_decimal *) item; - DBUG_PRINT("info", ("value %f", decimal_item->val_real())); + DBUG_PRINT("info", ("value %f", + ((Item_decimal*) item)->val_real())); NDB_ITEM_QUALIFICATION q; q.value_type= Item::DECIMAL_ITEM; curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item); @@ -9336,25 +9746,24 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, break; Ndb_item *a= cond->next->ndb_item; Ndb_item *b, *field, *value= NULL; - LINT_INIT(field); switch (cond->ndb_item->argument_count()) { case 1: - field= - (a->type == NDB_FIELD)? a : NULL; + field= (a->type == NDB_FIELD)? a : NULL; break; case 2: if (!cond->next->next) + { + field= NULL; break; + } b= cond->next->next->ndb_item; - value= - (a->type == NDB_VALUE)? a - : (b->type == NDB_VALUE)? b - : NULL; - field= - (a->type == NDB_FIELD)? a - : (b->type == NDB_FIELD)? b - : NULL; + value= ((a->type == NDB_VALUE) ? a : + (b->type == NDB_VALUE) ? b : + NULL); + field= ((a->type == NDB_FIELD) ? a : + (b->type == NDB_FIELD) ? b : + NULL); break; default: field= NULL; //Keep compiler happy @@ -9564,6 +9973,7 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, DBUG_RETURN(1); } + int ha_ndbcluster::build_scan_filter_group(Ndb_cond* &cond, NdbScanFilter *filter) { @@ -9637,6 +10047,7 @@ ha_ndbcluster::build_scan_filter_group(Ndb_cond* &cond, NdbScanFilter *filter) DBUG_RETURN(0); } + int ha_ndbcluster::build_scan_filter(Ndb_cond * &cond, NdbScanFilter *filter) { @@ -9672,31 +10083,12 @@ ha_ndbcluster::generate_scan_filter(Ndb_cond_stack *ndb_cond_stack, NdbScanOperation *op) { DBUG_ENTER("generate_scan_filter"); + if (ndb_cond_stack) { - DBUG_PRINT("info", ("Generating scan filter")); NdbScanFilter filter(op); - bool multiple_cond= FALSE; - // Wrap an AND group around multiple conditions - if (ndb_cond_stack->next) { - multiple_cond= TRUE; - if (filter.begin() == -1) - DBUG_RETURN(1); - } - for (Ndb_cond_stack *stack= ndb_cond_stack; - (stack); - stack= stack->next) - { - Ndb_cond *cond= stack->ndb_cond; - - if (build_scan_filter(cond, &filter)) - { - DBUG_PRINT("info", ("build_scan_filter failed")); - DBUG_RETURN(1); - } - } - if (multiple_cond && filter.end() == -1) - DBUG_RETURN(1); + + DBUG_RETURN(generate_scan_filter_from_cond(ndb_cond_stack, filter)); } else { @@ -9706,10 +10098,93 @@ ha_ndbcluster::generate_scan_filter(Ndb_cond_stack *ndb_cond_stack, DBUG_RETURN(0); } + +int +ha_ndbcluster::generate_scan_filter_from_cond(Ndb_cond_stack *ndb_cond_stack, + NdbScanFilter& filter) +{ + bool multiple_cond= FALSE; + DBUG_ENTER("generate_scan_filter_from_cond"); + + // Wrap an AND group around multiple conditions + if (ndb_cond_stack->next) + { + multiple_cond= TRUE; + if (filter.begin() == -1) + DBUG_RETURN(1); + } + for (Ndb_cond_stack *stack= ndb_cond_stack; + (stack); + stack= stack->next) + { + Ndb_cond *cond= stack->ndb_cond; + + if (build_scan_filter(cond, &filter)) + { + DBUG_PRINT("info", ("build_scan_filter failed")); + DBUG_RETURN(1); + } + } + if (multiple_cond && filter.end() == -1) + DBUG_RETURN(1); + + DBUG_RETURN(0); +} + + +int ha_ndbcluster::generate_scan_filter_from_key(NdbScanOperation *op, + const KEY* key_info, + const byte *key, + uint key_len, + byte *buf) +{ + KEY_PART_INFO* key_part= key_info->key_part; + KEY_PART_INFO* end= key_part+key_info->key_parts; + NdbScanFilter filter(op); + int res; + DBUG_ENTER("generate_scan_filter_from_key"); + + filter.begin(NdbScanFilter::AND); + for (; key_part != end; key_part++) + { + Field* field= key_part->field; + uint32 pack_len= field->pack_length(); + const byte* ptr= key; + DBUG_PRINT("info", ("Filtering value for %s", field->field_name)); + DBUG_DUMP("key", (char*)ptr, pack_len); + if (key_part->null_bit) + { + DBUG_PRINT("info", ("Generating ISNULL filter")); + if (filter.isnull(key_part->fieldnr-1) == -1) + DBUG_RETURN(1); + } + else + { + DBUG_PRINT("info", ("Generating EQ filter")); + if (filter.cmp(NdbScanFilter::COND_EQ, + key_part->fieldnr-1, + ptr, + pack_len) == -1) + DBUG_RETURN(1); + } + key += key_part->store_length; + } + // Add any pushed condition + if (m_cond_stack && + (res= generate_scan_filter_from_cond(m_cond_stack, filter))) + DBUG_RETURN(res); + + if (filter.end() == -1) + DBUG_RETURN(1); + + DBUG_RETURN(0); +} + + /* get table space info for SHOW CREATE TABLE */ -char* ha_ndbcluster::get_tablespace_name(THD *thd) +char* ha_ndbcluster::get_tablespace_name(THD *thd, char* name, uint name_len) { Ndb *ndb= check_ndb_in_thd(thd); NDBDICT *ndbdict= ndb->getDictionary(); @@ -9727,7 +10202,14 @@ char* ha_ndbcluster::get_tablespace_name(THD *thd) ndberr= ndbdict->getNdbError(); if(ndberr.classification != NdbError::NoError) goto err; - return (my_strdup(ts.getName(), MYF(0))); + DBUG_PRINT("info", ("Found tablespace '%s'", ts.getName())); + if (name) + { + strxnmov(name, name_len, ts.getName(), NullS); + return name; + } + else + return (my_strdup(ts.getName(), MYF(0))); } err: if (ndberr.status == NdbError::TemporaryError) @@ -9764,12 +10246,12 @@ ndbcluster_show_status(handlerton *hton, THD* thd, stat_print_fn *stat_print, update_status_variables(g_ndb_cluster_connection); buflen= my_snprintf(buf, sizeof(buf), - "cluster_node_id=%u, " + "cluster_node_id=%ld, " "connected_host=%s, " - "connected_port=%u, " - "number_of_data_nodes=%u, " - "number_of_ready_data_nodes=%u, " - "connect_count=%u", + "connected_port=%ld, " + "number_of_data_nodes=%ld, " + "number_of_ready_data_nodes=%ld, " + "connect_count=%ld", ndb_cluster_node_id, ndb_connected_host, ndb_connected_port, @@ -9845,13 +10327,13 @@ static bool adjusted_frag_count(uint no_fragments, uint no_nodes, return (reported_frags < no_fragments); } -int ha_ndbcluster::get_default_no_partitions(HA_CREATE_INFO *info) +int ha_ndbcluster::get_default_no_partitions(HA_CREATE_INFO *create_info) { ha_rows max_rows, min_rows; - if (info) + if (create_info) { - max_rows= info->max_rows; - min_rows= info->min_rows; + max_rows= create_info->max_rows; + min_rows= create_info->min_rows; } else { @@ -10002,15 +10484,14 @@ uint ha_ndbcluster::set_up_partition_info(partition_info *part_info, { uint16 frag_data[MAX_PARTITIONS]; char *ts_names[MAX_PARTITIONS]; - ulong ts_index= 0, fd_index= 0, i, j; + ulong fd_index= 0, i, j; NDBTAB *tab= (NDBTAB*)tab_par; NDBTAB::FragmentType ftype= NDBTAB::UserDefined; partition_element *part_elem; bool first= TRUE; - uint ts_id, ts_version, part_count= 0, tot_ts_name_len; + uint tot_ts_name_len; List_iterator<partition_element> part_it(part_info->partitions); int error; - char *name_ptr; DBUG_ENTER("ha_ndbcluster::set_up_partition_info"); if (part_info->part_type == HASH_PARTITION && @@ -10124,7 +10605,7 @@ uint ha_ndbcluster::set_up_partition_info(partition_info *part_info, } -bool ha_ndbcluster::check_if_incompatible_data(HA_CREATE_INFO *info, +bool ha_ndbcluster::check_if_incompatible_data(HA_CREATE_INFO *create_info, uint table_changes) { DBUG_ENTER("ha_ndbcluster::check_if_incompatible_data"); @@ -10137,6 +10618,8 @@ bool ha_ndbcluster::check_if_incompatible_data(HA_CREATE_INFO *info, DBUG_RETURN(COMPATIBLE_DATA_NO); } + int pk= 0; + int ai= 0; for (i= 0; i < table->s->fields; i++) { Field *field= table->field[i]; @@ -10152,81 +10635,106 @@ bool ha_ndbcluster::check_if_incompatible_data(HA_CREATE_INFO *info, DBUG_PRINT("info", ("add/drop index not supported for disk stored column")); DBUG_RETURN(COMPATIBLE_DATA_NO); } + + if (field->flags & PRI_KEY_FLAG) + pk=1; + if (field->flags & FIELD_IN_ADD_INDEX) + ai=1; } if (table_changes != IS_EQUAL_YES) DBUG_RETURN(COMPATIBLE_DATA_NO); + /** + * Changing from/to primary key + * + * This is _not_ correct, but check_if_incompatible_data-interface + * doesnt give more info, so I guess that we can't do any + * online add index if not using primary key + * + * This as mysql will handle a unique not null index as primary + * even wo/ user specifiying it... :-( + * + */ + if ((table_share->primary_key == MAX_KEY && pk) || + (table_share->primary_key != MAX_KEY && !pk) || + (table_share->primary_key == MAX_KEY && !pk && ai)) + { + DBUG_RETURN(COMPATIBLE_DATA_NO); + } + /* Check that auto_increment value was not changed */ - if ((info->used_fields & HA_CREATE_USED_AUTO) && - info->auto_increment_value != 0) + if ((create_info->used_fields & HA_CREATE_USED_AUTO) && + create_info->auto_increment_value != 0) DBUG_RETURN(COMPATIBLE_DATA_NO); /* Check that row format didn't change */ - if ((info->used_fields & HA_CREATE_USED_AUTO) && - get_row_type() != info->row_type) + if ((create_info->used_fields & HA_CREATE_USED_AUTO) && + get_row_type() != create_info->row_type) DBUG_RETURN(COMPATIBLE_DATA_NO); DBUG_RETURN(COMPATIBLE_DATA_YES); } -bool set_up_tablespace(st_alter_tablespace *info, +bool set_up_tablespace(st_alter_tablespace *alter_info, NdbDictionary::Tablespace *ndb_ts) { - ndb_ts->setName(info->tablespace_name); - ndb_ts->setExtentSize(info->extent_size); - ndb_ts->setDefaultLogfileGroup(info->logfile_group_name); - return false; + ndb_ts->setName(alter_info->tablespace_name); + ndb_ts->setExtentSize(alter_info->extent_size); + ndb_ts->setDefaultLogfileGroup(alter_info->logfile_group_name); + return FALSE; } -bool set_up_datafile(st_alter_tablespace *info, +bool set_up_datafile(st_alter_tablespace *alter_info, NdbDictionary::Datafile *ndb_df) { - if (info->max_size > 0) + if (alter_info->max_size > 0) { my_error(ER_TABLESPACE_AUTO_EXTEND_ERROR, MYF(0)); - return true; + return TRUE; } - ndb_df->setPath(info->data_file_name); - ndb_df->setSize(info->initial_size); - ndb_df->setTablespace(info->tablespace_name); - return false; + ndb_df->setPath(alter_info->data_file_name); + ndb_df->setSize(alter_info->initial_size); + ndb_df->setTablespace(alter_info->tablespace_name); + return FALSE; } -bool set_up_logfile_group(st_alter_tablespace *info, +bool set_up_logfile_group(st_alter_tablespace *alter_info, NdbDictionary::LogfileGroup *ndb_lg) { - ndb_lg->setName(info->logfile_group_name); - ndb_lg->setUndoBufferSize(info->undo_buffer_size); - return false; + ndb_lg->setName(alter_info->logfile_group_name); + ndb_lg->setUndoBufferSize(alter_info->undo_buffer_size); + return FALSE; } -bool set_up_undofile(st_alter_tablespace *info, +bool set_up_undofile(st_alter_tablespace *alter_info, NdbDictionary::Undofile *ndb_uf) { - ndb_uf->setPath(info->undo_file_name); - ndb_uf->setSize(info->initial_size); - ndb_uf->setLogfileGroup(info->logfile_group_name); - return false; + ndb_uf->setPath(alter_info->undo_file_name); + ndb_uf->setSize(alter_info->initial_size); + ndb_uf->setLogfileGroup(alter_info->logfile_group_name); + return FALSE; } -int ndbcluster_alter_tablespace(handlerton *hton, THD* thd, st_alter_tablespace *info) +int ndbcluster_alter_tablespace(handlerton *hton, + THD* thd, st_alter_tablespace *alter_info) { + int is_tablespace= 0; + NdbError err; + NDBDICT *dict; + int error; + const char *errmsg; + Ndb *ndb; DBUG_ENTER("ha_ndbcluster::alter_tablespace"); + LINT_INIT(errmsg); - int is_tablespace= 0; - Ndb *ndb= check_ndb_in_thd(thd); + ndb= check_ndb_in_thd(thd); if (ndb == NULL) { DBUG_RETURN(HA_ERR_NO_CONNECTION); } + dict= ndb->getDictionary(); - NdbError err; - NDBDICT *dict= ndb->getDictionary(); - int error; - const char * errmsg; - LINT_INIT(errmsg); - - switch (info->ts_cmd_type){ + switch (alter_info->ts_cmd_type){ case (CREATE_TABLESPACE): { error= ER_CREATE_FILEGROUP_FAILED; @@ -10234,11 +10742,11 @@ int ndbcluster_alter_tablespace(handlerton *hton, THD* thd, st_alter_tablespace NdbDictionary::Tablespace ndb_ts; NdbDictionary::Datafile ndb_df; NdbDictionary::ObjectId objid; - if (set_up_tablespace(info, &ndb_ts)) + if (set_up_tablespace(alter_info, &ndb_ts)) { DBUG_RETURN(1); } - if (set_up_datafile(info, &ndb_df)) + if (set_up_datafile(alter_info, &ndb_df)) { DBUG_RETURN(1); } @@ -10248,7 +10756,7 @@ int ndbcluster_alter_tablespace(handlerton *hton, THD* thd, st_alter_tablespace DBUG_PRINT("error", ("createTablespace returned %d", error)); goto ndberror; } - DBUG_PRINT("info", ("Successfully created Tablespace")); + DBUG_PRINT("alter_info", ("Successfully created Tablespace")); errmsg= "DATAFILE"; if (dict->createDatafile(ndb_df)) { @@ -10270,10 +10778,10 @@ int ndbcluster_alter_tablespace(handlerton *hton, THD* thd, st_alter_tablespace case (ALTER_TABLESPACE): { error= ER_ALTER_FILEGROUP_FAILED; - if (info->ts_alter_tablespace_type == ALTER_TABLESPACE_ADD_FILE) + if (alter_info->ts_alter_tablespace_type == ALTER_TABLESPACE_ADD_FILE) { NdbDictionary::Datafile ndb_df; - if (set_up_datafile(info, &ndb_df)) + if (set_up_datafile(alter_info, &ndb_df)) { DBUG_RETURN(1); } @@ -10283,14 +10791,14 @@ int ndbcluster_alter_tablespace(handlerton *hton, THD* thd, st_alter_tablespace goto ndberror; } } - else if(info->ts_alter_tablespace_type == ALTER_TABLESPACE_DROP_FILE) + else if(alter_info->ts_alter_tablespace_type == ALTER_TABLESPACE_DROP_FILE) { - NdbDictionary::Tablespace ts= dict->getTablespace(info->tablespace_name); - NdbDictionary::Datafile df= dict->getDatafile(0, info->data_file_name); + NdbDictionary::Tablespace ts= dict->getTablespace(alter_info->tablespace_name); + NdbDictionary::Datafile df= dict->getDatafile(0, alter_info->data_file_name); NdbDictionary::ObjectId objid; df.getTablespaceId(&objid); if (ts.getObjectId() == objid.getObjectId() && - strcmp(df.getPath(), info->data_file_name) == 0) + strcmp(df.getPath(), alter_info->data_file_name) == 0) { errmsg= " DROP DATAFILE"; if (dict->dropDatafile(df)) @@ -10308,7 +10816,7 @@ int ndbcluster_alter_tablespace(handlerton *hton, THD* thd, st_alter_tablespace else { DBUG_PRINT("error", ("Unsupported alter tablespace: %d", - info->ts_alter_tablespace_type)); + alter_info->ts_alter_tablespace_type)); DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED); } is_tablespace= 1; @@ -10320,14 +10828,14 @@ int ndbcluster_alter_tablespace(handlerton *hton, THD* thd, st_alter_tablespace NdbDictionary::LogfileGroup ndb_lg; NdbDictionary::Undofile ndb_uf; NdbDictionary::ObjectId objid; - if (info->undo_file_name == NULL) + if (alter_info->undo_file_name == NULL) { /* REDO files in LOGFILE GROUP not supported yet */ DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED); } - if (set_up_logfile_group(info, &ndb_lg)) + if (set_up_logfile_group(alter_info, &ndb_lg)) { DBUG_RETURN(1); } @@ -10336,8 +10844,8 @@ int ndbcluster_alter_tablespace(handlerton *hton, THD* thd, st_alter_tablespace { goto ndberror; } - DBUG_PRINT("info", ("Successfully created Logfile Group")); - if (set_up_undofile(info, &ndb_uf)) + DBUG_PRINT("alter_info", ("Successfully created Logfile Group")); + if (set_up_undofile(alter_info, &ndb_uf)) { DBUG_RETURN(1); } @@ -10359,7 +10867,7 @@ int ndbcluster_alter_tablespace(handlerton *hton, THD* thd, st_alter_tablespace case (ALTER_LOGFILE_GROUP): { error= ER_ALTER_FILEGROUP_FAILED; - if (info->undo_file_name == NULL) + if (alter_info->undo_file_name == NULL) { /* REDO files in LOGFILE GROUP not supported yet @@ -10367,7 +10875,7 @@ int ndbcluster_alter_tablespace(handlerton *hton, THD* thd, st_alter_tablespace DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED); } NdbDictionary::Undofile ndb_uf; - if (set_up_undofile(info, &ndb_uf)) + if (set_up_undofile(alter_info, &ndb_uf)) { DBUG_RETURN(1); } @@ -10382,7 +10890,7 @@ int ndbcluster_alter_tablespace(handlerton *hton, THD* thd, st_alter_tablespace { error= ER_DROP_FILEGROUP_FAILED; errmsg= "TABLESPACE"; - if (dict->dropTablespace(dict->getTablespace(info->tablespace_name))) + if (dict->dropTablespace(dict->getTablespace(alter_info->tablespace_name))) { goto ndberror; } @@ -10393,7 +10901,7 @@ int ndbcluster_alter_tablespace(handlerton *hton, THD* thd, st_alter_tablespace { error= ER_DROP_FILEGROUP_FAILED; errmsg= "LOGFILE GROUP"; - if (dict->dropLogfileGroup(dict->getLogfileGroup(info->logfile_group_name))) + if (dict->dropLogfileGroup(dict->getLogfileGroup(alter_info->logfile_group_name))) { goto ndberror; } @@ -10416,13 +10924,13 @@ int ndbcluster_alter_tablespace(handlerton *hton, THD* thd, st_alter_tablespace if (is_tablespace) ndbcluster_log_schema_op(thd, 0, thd->query, thd->query_length, - "", info->tablespace_name, + "", alter_info->tablespace_name, 0, 0, SOT_TABLESPACE, 0, 0, 0); else ndbcluster_log_schema_op(thd, 0, thd->query, thd->query_length, - "", info->logfile_group_name, + "", alter_info->logfile_group_name, 0, 0, SOT_LOGFILE_GROUP, 0, 0, 0); #endif @@ -10443,7 +10951,6 @@ bool ha_ndbcluster::get_no_parts(const char *name, uint *no_parts) { Ndb *ndb; NDBDICT *dict; - const NDBTAB *tab; int err; DBUG_ENTER("ha_ndbcluster::get_no_parts"); LINT_INIT(err); @@ -10498,7 +11005,7 @@ static int ndbcluster_fill_files_table(handlerton *hton, while ((id= g_ndb_cluster_connection->get_next_node(iter))) { - uint c= 0; + init_fill_schema_files_row(table); NdbDictionary::Datafile df= dict->getDatafile(id, elt.name); ndberr= dict->getNdbError(); if(ndberr.classification != NdbError::NoError) @@ -10516,76 +11023,48 @@ static int ndbcluster_fill_files_table(handlerton *hton, ERR_RETURN(ndberr); } - table->field[c++]->set_null(); // FILE_ID - table->field[c]->set_notnull(); - table->field[c++]->store(elt.name, strlen(elt.name), - system_charset_info); - table->field[c]->set_notnull(); - table->field[c++]->store("DATAFILE",8,system_charset_info); - table->field[c]->set_notnull(); - table->field[c++]->store(df.getTablespace(), strlen(df.getTablespace()), - system_charset_info); - table->field[c++]->set_null(); // TABLE_CATALOG - table->field[c++]->set_null(); // TABLE_SCHEMA - table->field[c++]->set_null(); // TABLE_NAME - - // LOGFILE_GROUP_NAME - table->field[c]->set_notnull(); - table->field[c++]->store(ts.getDefaultLogfileGroup(), - strlen(ts.getDefaultLogfileGroup()), - system_charset_info); - table->field[c++]->set_null(); // LOGFILE_GROUP_NUMBER - table->field[c]->set_notnull(); - table->field[c++]->store(ndbcluster_hton_name, - ndbcluster_hton_name_length, - system_charset_info); // ENGINE - - table->field[c++]->set_null(); // FULLTEXT_KEYS - table->field[c++]->set_null(); // DELETED_ROWS - table->field[c++]->set_null(); // UPDATE_COUNT - table->field[c]->set_notnull(); - table->field[c++]->store(df.getFree() / ts.getExtentSize()); // FREE_EXTENTS - table->field[c]->set_notnull(); - table->field[c++]->store(df.getSize() / ts.getExtentSize()); // TOTAL_EXTENTS - table->field[c]->set_notnull(); - table->field[c++]->store(ts.getExtentSize()); // EXTENT_SIZE - - table->field[c]->set_notnull(); - table->field[c++]->store(df.getSize()); // INITIAL_SIZE - table->field[c]->set_notnull(); - table->field[c++]->store(df.getSize()); // MAXIMUM_SIZE - table->field[c++]->set_null(); // AUTOEXTEND_SIZE - - table->field[c++]->set_null(); // CREATION_TIME - table->field[c++]->set_null(); // LAST_UPDATE_TIME - table->field[c++]->set_null(); // LAST_ACCESS_TIME - table->field[c++]->set_null(); // RECOVER_TIME - table->field[c++]->set_null(); // TRANSACTION_COUNTER - - table->field[c]->set_notnull(); - table->field[c++]->store(df.getObjectVersion()); // VERSION - - table->field[c]->set_notnull(); - table->field[c++]->store("FIXED", 5, system_charset_info); // ROW_FORMAT - - table->field[c++]->set_null(); // TABLE_ROWS - table->field[c++]->set_null(); // AVG_ROW_LENGTH - table->field[c++]->set_null(); // DATA_LENGTH - table->field[c++]->set_null(); // MAX_DATA_LENGTH - table->field[c++]->set_null(); // INDEX_LENGTH - table->field[c++]->set_null(); // DATA_FREE - table->field[c++]->set_null(); // CREATE_TIME - table->field[c++]->set_null(); // UPDATE_TIME - table->field[c++]->set_null(); // CHECK_TIME - table->field[c++]->set_null(); // CHECKSUM - - table->field[c]->set_notnull(); - table->field[c++]->store("NORMAL", 6, system_charset_info); + table->field[IS_FILES_FILE_NAME]->set_notnull(); + table->field[IS_FILES_FILE_NAME]->store(elt.name, strlen(elt.name), + system_charset_info); + table->field[IS_FILES_FILE_TYPE]->set_notnull(); + table->field[IS_FILES_FILE_TYPE]->store("DATAFILE",8, + system_charset_info); + table->field[IS_FILES_TABLESPACE_NAME]->set_notnull(); + table->field[IS_FILES_TABLESPACE_NAME]->store(df.getTablespace(), + strlen(df.getTablespace()), + system_charset_info); + table->field[IS_FILES_LOGFILE_GROUP_NAME]->set_notnull(); + table->field[IS_FILES_LOGFILE_GROUP_NAME]-> + store(ts.getDefaultLogfileGroup(), + strlen(ts.getDefaultLogfileGroup()), + system_charset_info); + table->field[IS_FILES_ENGINE]->set_notnull(); + table->field[IS_FILES_ENGINE]->store(ndbcluster_hton_name, + ndbcluster_hton_name_length, + system_charset_info); + + table->field[IS_FILES_FREE_EXTENTS]->set_notnull(); + table->field[IS_FILES_FREE_EXTENTS]->store(df.getFree() + / ts.getExtentSize()); + table->field[IS_FILES_TOTAL_EXTENTS]->set_notnull(); + table->field[IS_FILES_TOTAL_EXTENTS]->store(df.getSize() + / ts.getExtentSize()); + table->field[IS_FILES_EXTENT_SIZE]->set_notnull(); + table->field[IS_FILES_EXTENT_SIZE]->store(ts.getExtentSize()); + table->field[IS_FILES_INITIAL_SIZE]->set_notnull(); + table->field[IS_FILES_INITIAL_SIZE]->store(df.getSize()); + table->field[IS_FILES_MAXIMUM_SIZE]->set_notnull(); + table->field[IS_FILES_MAXIMUM_SIZE]->store(df.getSize()); + table->field[IS_FILES_VERSION]->set_notnull(); + table->field[IS_FILES_VERSION]->store(df.getObjectVersion()); + + table->field[IS_FILES_ROW_FORMAT]->set_notnull(); + table->field[IS_FILES_ROW_FORMAT]->store("FIXED", 5, system_charset_info); char extra[30]; int len= my_snprintf(extra, sizeof(extra), "CLUSTER_NODE=%u", id); - table->field[c]->store(extra, len, system_charset_info); - table->field[c]->set_notnull(); + table->field[IS_FILES_EXTRA]->set_notnull(); + table->field[IS_FILES_EXTRA]->store(extra, len, system_charset_info); schema_table_store_record(thd, table); } } @@ -10624,76 +11103,44 @@ static int ndbcluster_fill_files_table(handlerton *hton, ERR_RETURN(ndberr); } - int c= 0; - table->field[c++]->set_null(); // FILE_ID - table->field[c]->set_notnull(); - table->field[c++]->store(elt.name, strlen(elt.name), - system_charset_info); - table->field[c]->set_notnull(); - table->field[c++]->store("UNDO LOG", 8, system_charset_info); - table->field[c++]->set_null(); // TABLESPACE NAME - table->field[c++]->set_null(); // TABLE_CATALOG - table->field[c++]->set_null(); // TABLE_SCHEMA - table->field[c++]->set_null(); // TABLE_NAME - - // LOGFILE_GROUP_NAME + init_fill_schema_files_row(table); + table->field[IS_FILES_FILE_NAME]->set_notnull(); + table->field[IS_FILES_FILE_NAME]->store(elt.name, strlen(elt.name), + system_charset_info); + table->field[IS_FILES_FILE_TYPE]->set_notnull(); + table->field[IS_FILES_FILE_TYPE]->store("UNDO LOG", 8, + system_charset_info); NdbDictionary::ObjectId objid; uf.getLogfileGroupId(&objid); - table->field[c]->set_notnull(); - table->field[c++]->store(uf.getLogfileGroup(), - strlen(uf.getLogfileGroup()), - system_charset_info); - table->field[c]->set_notnull(); - table->field[c++]->store(objid.getObjectId()); // LOGFILE_GROUP_NUMBER - table->field[c]->set_notnull(); - table->field[c++]->store(ndbcluster_hton_name, - ndbcluster_hton_name_length, - system_charset_info); // ENGINE - - table->field[c++]->set_null(); // FULLTEXT_KEYS - table->field[c++]->set_null(); // DELETED_ROWS - table->field[c++]->set_null(); // UPDATE_COUNT - table->field[c++]->set_null(); // FREE_EXTENTS - table->field[c]->set_notnull(); - table->field[c++]->store(uf.getSize()/4); // TOTAL_EXTENTS - table->field[c]->set_notnull(); - table->field[c++]->store(4); // EXTENT_SIZE - - table->field[c]->set_notnull(); - table->field[c++]->store(uf.getSize()); // INITIAL_SIZE - table->field[c]->set_notnull(); - table->field[c++]->store(uf.getSize()); // MAXIMUM_SIZE - table->field[c++]->set_null(); // AUTOEXTEND_SIZE - - table->field[c++]->set_null(); // CREATION_TIME - table->field[c++]->set_null(); // LAST_UPDATE_TIME - table->field[c++]->set_null(); // LAST_ACCESS_TIME - table->field[c++]->set_null(); // RECOVER_TIME - table->field[c++]->set_null(); // TRANSACTION_COUNTER - - table->field[c]->set_notnull(); - table->field[c++]->store(uf.getObjectVersion()); // VERSION - - table->field[c++]->set_null(); // ROW FORMAT - - table->field[c++]->set_null(); // TABLE_ROWS - table->field[c++]->set_null(); // AVG_ROW_LENGTH - table->field[c++]->set_null(); // DATA_LENGTH - table->field[c++]->set_null(); // MAX_DATA_LENGTH - table->field[c++]->set_null(); // INDEX_LENGTH - table->field[c++]->set_null(); // DATA_FREE - table->field[c++]->set_null(); // CREATE_TIME - table->field[c++]->set_null(); // UPDATE_TIME - table->field[c++]->set_null(); // CHECK_TIME - table->field[c++]->set_null(); // CHECKSUM - - table->field[c]->set_notnull(); - table->field[c++]->store("NORMAL", 6, system_charset_info); + table->field[IS_FILES_LOGFILE_GROUP_NAME]->set_notnull(); + table->field[IS_FILES_LOGFILE_GROUP_NAME]->store(uf.getLogfileGroup(), + strlen(uf.getLogfileGroup()), + system_charset_info); + table->field[IS_FILES_LOGFILE_GROUP_NUMBER]->set_notnull(); + table->field[IS_FILES_LOGFILE_GROUP_NUMBER]->store(objid.getObjectId()); + table->field[IS_FILES_ENGINE]->set_notnull(); + table->field[IS_FILES_ENGINE]->store(ndbcluster_hton_name, + ndbcluster_hton_name_length, + system_charset_info); + + table->field[IS_FILES_TOTAL_EXTENTS]->set_notnull(); + table->field[IS_FILES_TOTAL_EXTENTS]->store(uf.getSize()/4); + table->field[IS_FILES_EXTENT_SIZE]->set_notnull(); + table->field[IS_FILES_EXTENT_SIZE]->store(4); + + table->field[IS_FILES_INITIAL_SIZE]->set_notnull(); + table->field[IS_FILES_INITIAL_SIZE]->store(uf.getSize()); + table->field[IS_FILES_MAXIMUM_SIZE]->set_notnull(); + table->field[IS_FILES_MAXIMUM_SIZE]->store(uf.getSize()); + + table->field[IS_FILES_VERSION]->set_notnull(); + table->field[IS_FILES_VERSION]->store(uf.getObjectVersion()); char extra[100]; - int len= my_snprintf(extra,sizeof(extra),"CLUSTER_NODE=%u;UNDO_BUFFER_SIZE=%lu",id,lfg.getUndoBufferSize()); - table->field[c]->set_notnull(); - table->field[c]->store(extra, len, system_charset_info); + int len= my_snprintf(extra,sizeof(extra),"CLUSTER_NODE=%u;UNDO_BUFFER_SIZE=%lu", + id, (ulong) lfg.getUndoBufferSize()); + table->field[IS_FILES_EXTRA]->set_notnull(); + table->field[IS_FILES_EXTRA]->store(extra, len, system_charset_info); schema_table_store_record(thd, table); } } @@ -10708,7 +11155,6 @@ static int ndbcluster_fill_files_table(handlerton *hton, for (i= 0; i < lfglist.count; i++) { NdbDictionary::Dictionary::List::Element& elt= lfglist.elements[i]; - unsigned id; NdbDictionary::LogfileGroup lfg= dict->getLogfileGroup(elt.name); ndberr= dict->getNdbError(); @@ -10719,69 +11165,36 @@ static int ndbcluster_fill_files_table(handlerton *hton, ERR_RETURN(ndberr); } - int c= 0; - table->field[c++]->set_null(); // FILE_ID - table->field[c++]->set_null(); // name - table->field[c]->set_notnull(); - table->field[c++]->store("UNDO LOG", 8, system_charset_info); - table->field[c++]->set_null(); // TABLESPACE NAME - table->field[c++]->set_null(); // TABLE_CATALOG - table->field[c++]->set_null(); // TABLE_SCHEMA - table->field[c++]->set_null(); // TABLE_NAME - - // LOGFILE_GROUP_NAME - table->field[c]->set_notnull(); - table->field[c++]->store(elt.name, strlen(elt.name), - system_charset_info); - table->field[c]->set_notnull(); - table->field[c++]->store(lfg.getObjectId()); // LOGFILE_GROUP_NUMBER - table->field[c]->set_notnull(); - table->field[c++]->store(ndbcluster_hton_name, - ndbcluster_hton_name_length, - system_charset_info); // ENGINE - - table->field[c++]->set_null(); // FULLTEXT_KEYS - table->field[c++]->set_null(); // DELETED_ROWS - table->field[c++]->set_null(); // UPDATE_COUNT - table->field[c]->set_notnull(); - table->field[c++]->store(lfg.getUndoFreeWords()); // FREE_EXTENTS - table->field[c++]->set_null(); //store(uf.getSize()/4); // TOTAL_EXTENTS - table->field[c]->set_notnull(); - table->field[c++]->store(4); // EXTENT_SIZE - - table->field[c++]->set_null();//store(uf.getSize()); // INITIAL_SIZE - table->field[c++]->set_null(); //store(uf.getSize()); // MAXIMUM_SIZE - table->field[c++]->set_null(); // AUTOEXTEND_SIZE - - table->field[c++]->set_null(); // CREATION_TIME - table->field[c++]->set_null(); // LAST_UPDATE_TIME - table->field[c++]->set_null(); // LAST_ACCESS_TIME - table->field[c++]->set_null(); // RECOVER_TIME - table->field[c++]->set_null(); // TRANSACTION_COUNTER - - table->field[c]->set_notnull(); - table->field[c++]->store(lfg.getObjectVersion()); // VERSION - - table->field[c++]->set_null(); // ROW FORMAT - - table->field[c++]->set_null(); // TABLE_ROWS - table->field[c++]->set_null(); // AVG_ROW_LENGTH - table->field[c++]->set_null(); // DATA_LENGTH - table->field[c++]->set_null(); // MAX_DATA_LENGTH - table->field[c++]->set_null(); // INDEX_LENGTH - table->field[c++]->set_null(); // DATA_FREE - table->field[c++]->set_null(); // CREATE_TIME - table->field[c++]->set_null(); // UPDATE_TIME - table->field[c++]->set_null(); // CHECK_TIME - table->field[c++]->set_null(); // CHECKSUM - - table->field[c]->set_notnull(); - table->field[c++]->store("NORMAL", 6, system_charset_info); + init_fill_schema_files_row(table); + table->field[IS_FILES_FILE_TYPE]->set_notnull(); + table->field[IS_FILES_FILE_TYPE]->store("UNDO LOG", 8, + system_charset_info); + + table->field[IS_FILES_LOGFILE_GROUP_NAME]->set_notnull(); + table->field[IS_FILES_LOGFILE_GROUP_NAME]->store(elt.name, + strlen(elt.name), + system_charset_info); + table->field[IS_FILES_LOGFILE_GROUP_NUMBER]->set_notnull(); + table->field[IS_FILES_LOGFILE_GROUP_NUMBER]->store(lfg.getObjectId()); + table->field[IS_FILES_ENGINE]->set_notnull(); + table->field[IS_FILES_ENGINE]->store(ndbcluster_hton_name, + ndbcluster_hton_name_length, + system_charset_info); + + table->field[IS_FILES_FREE_EXTENTS]->set_notnull(); + table->field[IS_FILES_FREE_EXTENTS]->store(lfg.getUndoFreeWords()); + table->field[IS_FILES_EXTENT_SIZE]->set_notnull(); + table->field[IS_FILES_EXTENT_SIZE]->store(4); + + table->field[IS_FILES_VERSION]->set_notnull(); + table->field[IS_FILES_VERSION]->store(lfg.getObjectVersion()); char extra[100]; - int len= my_snprintf(extra,sizeof(extra),"UNDO_BUFFER_SIZE=%lu",id,lfg.getUndoBufferSize()); - table->field[c]->set_notnull(); - table->field[c]->store(extra, len, system_charset_info); + int len= my_snprintf(extra,sizeof(extra), + "UNDO_BUFFER_SIZE=%lu", + (ulong) lfg.getUndoBufferSize()); + table->field[IS_FILES_EXTRA]->set_notnull(); + table->field[IS_FILES_EXTRA]->store(extra, len, system_charset_info); schema_table_store_record(thd, table); } DBUG_RETURN(0); diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index 19ff513f9b1..63665fde0f8 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -73,6 +72,7 @@ typedef struct ndb_index_data { const NdbDictionary::Index *index; const NdbDictionary::Index *unique_index; unsigned char *unique_index_attrid_map; + bool null_in_unique_index; // In this version stats are not shared between threads NdbIndexStat* index_stat; uint index_stat_cache_entries; @@ -108,6 +108,7 @@ typedef struct st_ndbcluster_share { char *table_name; Ndb::TupleIdRange tuple_id_range; #ifdef HAVE_NDB_BINLOG + uint32 connect_count; uint32 flags; NdbEventOperation *op; NdbEventOperation *op_old; // for rename table @@ -670,10 +671,15 @@ class ha_ndbcluster: public handler KEY_MULTI_RANGE*ranges, uint range_count, bool sorted, HANDLER_BUFFER *buffer); int read_multi_range_next(KEY_MULTI_RANGE **found_range_p); + bool null_value_index_search(KEY_MULTI_RANGE *ranges, + KEY_MULTI_RANGE *end_range, + HANDLER_BUFFER *buffer); bool get_error_message(int error, String *buf); ha_rows records(); - void info(uint); + ha_rows estimate_rows_upper_bound() + { return HA_POS_ERROR; } + int info(uint); void get_dynamic_partition_info(PARTITION_INFO *stat_info, uint part_id); int extra(enum ha_extra_function operation); int extra_opt(enum ha_extra_function operation, ulong cache_size); @@ -772,6 +778,12 @@ static void set_tabname(const char *pathname, char *tabname); void cond_pop(); uint8 table_cache_type(); + + /* + * Internal to ha_ndbcluster, used by C functions + */ + int ndb_err(NdbTransaction*); + my_bool register_query_cache_table(THD *thd, char *table_key, uint key_length, qc_engine_callback *engine_callback, @@ -808,12 +820,13 @@ private: NDB_INDEX_TYPE get_index_type_from_table(uint index_no) const; NDB_INDEX_TYPE get_index_type_from_key(uint index_no, KEY *key_info, bool primary) const; - int check_index_fields_not_null(uint index_no); + bool has_null_in_unique_index(uint idx_no) const; + bool check_index_fields_not_null(KEY *key_info); uint set_up_partition_info(partition_info *part_info, TABLE *table, void *tab); - char* get_tablespace_name(THD *thd); + char* get_tablespace_name(THD *thd, char *name, uint name_len); int set_range_data(void *tab, partition_info* part_info); int set_list_data(void *tab, partition_info* part_info); int complemented_read(const byte *old_data, byte *new_data, @@ -823,15 +836,19 @@ private: const key_range *end_key, bool sorted, bool descending, byte* buf, part_id_range *part_spec); + int unique_index_read(const byte *key, uint key_len, + byte *buf); + int unique_index_scan(const KEY* key_info, + const byte *key, + uint key_len, + byte *buf); int full_table_scan(byte * buf); bool check_all_operations_for_error(NdbTransaction *trans, const NdbOperation *first, const NdbOperation *last, uint errcode); - int peek_indexed_rows(const byte *record); - int unique_index_read(const byte *key, uint key_len, - byte *buf); + int peek_indexed_rows(const byte *record, bool check_pk); int fetch_next(NdbScanOperation* op); int next_result(byte *buf); int define_read_attrs(byte* buf, NdbOperation* op); @@ -868,7 +885,6 @@ private: ulonglong nb_desired_values, ulonglong *first_value, ulonglong *nb_reserved_values); - int ndb_err(NdbTransaction*); bool uses_blob_value(); char *update_table_comment(const char * comment); @@ -878,7 +894,7 @@ private: int check_ndb_connection(THD* thd= current_thd); void set_rec_per_key(); - void records_update(); + int records_update(); void no_uncommitted_rows_execute_failure(); void no_uncommitted_rows_update(int); void no_uncommitted_rows_reset(THD *); @@ -898,6 +914,13 @@ private: int build_scan_filter(Ndb_cond* &cond, NdbScanFilter* filter); int generate_scan_filter(Ndb_cond_stack* cond_stack, NdbScanOperation* op); + int generate_scan_filter_from_cond(Ndb_cond_stack* cond_stack, + NdbScanFilter& filter); + int generate_scan_filter_from_key(NdbScanOperation* op, + const KEY* key_info, + const byte *key, + uint key_len, + byte *buf); friend int execute_commit(ha_ndbcluster*, NdbTransaction*); friend int execute_no_commit_ignore_no_key(ha_ndbcluster*, NdbTransaction*); diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 5f5c8bcb221..2615732e7ee 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -16,6 +15,7 @@ */ #include "mysql_priv.h" +#include "sql_show.h" #ifdef WITH_NDBCLUSTER_STORAGE_ENGINE #include "ha_ndbcluster.h" @@ -80,6 +80,9 @@ THD *injector_thd= 0; static Ndb *injector_ndb= 0; static Ndb *schema_ndb= 0; +static int ndbcluster_binlog_inited= 0; +static int ndbcluster_binlog_terminating= 0; + /* Mutex and condition used for interacting between client sql thread and injector thread @@ -93,8 +96,9 @@ static ulonglong ndb_latest_applied_binlog_epoch= 0; static ulonglong ndb_latest_handled_binlog_epoch= 0; static ulonglong ndb_latest_received_binlog_epoch= 0; -NDB_SHARE *apply_status_share= 0; -NDB_SHARE *schema_share= 0; +NDB_SHARE *ndb_apply_status_share= 0; +NDB_SHARE *ndb_schema_share= 0; +pthread_mutex_t ndb_schema_share_mutex; /* Schema object distribution handling */ HASH ndb_schema_objects; @@ -115,9 +119,9 @@ static void ndb_free_schema_object(NDB_SCHEMA_OBJECT **ndb_schema_object, static Uint64 *p_latest_trans_gci= 0; /* - Global variables for holding the binlog_index table reference + Global variables for holding the ndb_binlog_index table reference */ -static TABLE *binlog_index= 0; +static TABLE *ndb_binlog_index= 0; static TABLE_LIST binlog_tables; /* @@ -161,16 +165,16 @@ static void dbug_print_table(const char *info, TABLE *table) } DBUG_PRINT("info", ("%s: %s.%s s->fields: %d " - "reclength: %d rec_buff_length: %d record[0]: %lx " - "record[1]: %lx", + "reclength: %lu rec_buff_length: %u record[0]: 0x%lx " + "record[1]: 0x%lx", info, table->s->db.str, table->s->table_name.str, table->s->fields, table->s->reclength, table->s->rec_buff_length, - table->record[0], - table->record[1])); + (long) table->record[0], + (long) table->record[1])); for (unsigned int i= 0; i < table->s->fields; i++) { @@ -180,7 +184,7 @@ static void dbug_print_table(const char *info, TABLE *table) "ptr: 0x%lx[+%d] null_bit: %u null_ptr: 0x%lx[+%d]", i, f->field_name, - f->flags, + (long) f->flags, (f->flags & PRI_KEY_FLAG) ? "pri" : "attr", (f->flags & NOT_NULL_FLAG) ? "" : ",nullable", (f->flags & UNSIGNED_FLAG) ? ",unsigned" : ",signed", @@ -189,16 +193,18 @@ static void dbug_print_table(const char *info, TABLE *table) (f->flags & BINARY_FLAG) ? ",binary" : "", f->real_type(), f->pack_length(), - f->ptr, f->ptr - table->record[0], + (long) f->ptr, (int) (f->ptr - table->record[0]), f->null_bit, - f->null_ptr, (byte*) f->null_ptr - table->record[0])); + (long) f->null_ptr, + (int) ((byte*) f->null_ptr - table->record[0]))); if (f->type() == MYSQL_TYPE_BIT) { Field_bit *g= (Field_bit*) f; DBUG_PRINT("MYSQL_TYPE_BIT",("field_length: %d bit_ptr: 0x%lx[+%d] " - "bit_ofs: %u bit_len: %u", - g->field_length, g->bit_ptr, - (byte*) g->bit_ptr-table->record[0], + "bit_ofs: %d bit_len: %u", + g->field_length, (long) g->bit_ptr, + (int) ((byte*) g->bit_ptr - + table->record[0]), g->bit_ofs, g->bit_len)); } } @@ -212,8 +218,8 @@ static void dbug_print_table(const char *info, TABLE *table) Run a query through mysql_parse Used to: - - purging the binlog_index - - creating the apply_status table + - purging the ndb_binlog_index + - creating the ndb_apply_status table */ static void run_query(THD *thd, char *buf, char *end, my_bool print_error, my_bool disable_binlog) @@ -251,10 +257,10 @@ static void run_query(THD *thd, char *buf, char *end, if (thd == injector_thd) { /* - running the query will close all tables, including the binlog_index + running the query will close all tables, including the ndb_binlog_index used in injector_thd */ - binlog_index= 0; + ndb_binlog_index= 0; } } @@ -357,13 +363,19 @@ void ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *_table) int do_event_op= ndb_binlog_running; DBUG_ENTER("ndbcluster_binlog_init_share"); + share->connect_count= g_ndb_cluster_connection->get_connect_count(); + share->op= 0; share->table= 0; - if (!schema_share && + if (!ndb_schema_share && strcmp(share->db, NDB_REP_DB) == 0 && strcmp(share->table_name, NDB_SCHEMA_TABLE) == 0) do_event_op= 1; + else if (!ndb_apply_status_share && + strcmp(share->db, NDB_REP_DB) == 0 && + strcmp(share->table_name, NDB_APPLY_TABLE) == 0) + do_event_op= 1; { int i, no_nodes= g_ndb_cluster_connection->no_db_nodes(); @@ -555,67 +567,34 @@ ndbcluster_binlog_log_query(handlerton *hton, THD *thd, enum_binlog_command binl DBUG_VOID_RETURN; } + /* - End use of the NDB Cluster table handler - - free all global variables allocated by - ndbcluster_init() + End use of the NDB Cluster binlog + - wait for binlog thread to shutdown */ static int ndbcluster_binlog_end(THD *thd) { - DBUG_ENTER("ndb_binlog_end"); + DBUG_ENTER("ndbcluster_binlog_end"); - if (!ndbcluster_util_inited) + if (!ndbcluster_binlog_inited) DBUG_RETURN(0); - - // Kill ndb utility thread - (void) pthread_mutex_lock(&LOCK_ndb_util_thread); - DBUG_PRINT("exit",("killing ndb util thread: %lx", ndb_util_thread)); - (void) pthread_cond_signal(&COND_ndb_util_thread); - (void) pthread_mutex_unlock(&LOCK_ndb_util_thread); + ndbcluster_binlog_inited= 0; #ifdef HAVE_NDB_BINLOG /* wait for injector thread to finish */ - if (ndb_binlog_thread_running > 0) - { - pthread_mutex_lock(&injector_mutex); - while (ndb_binlog_thread_running > 0) - { - struct timespec abstime; - set_timespec(abstime, 1); - pthread_cond_timedwait(&injector_cond, &injector_mutex, &abstime); - } - pthread_mutex_unlock(&injector_mutex); - } + ndbcluster_binlog_terminating= 1; + pthread_cond_signal(&injector_cond); + pthread_mutex_lock(&injector_mutex); + while (ndb_binlog_thread_running > 0) + pthread_cond_wait(&injector_cond, &injector_mutex); + pthread_mutex_unlock(&injector_mutex); - /* remove all shares */ - { - pthread_mutex_lock(&ndbcluster_mutex); - for (uint i= 0; i < ndbcluster_open_tables.records; i++) - { - NDB_SHARE *share= - (NDB_SHARE*) hash_element(&ndbcluster_open_tables, i); - if (share->table) - DBUG_PRINT("share", - ("table->s->db.table_name: %s.%s", - share->table->s->db.str, share->table->s->table_name.str)); - if (share->state != NSS_DROPPED && !--share->use_count) - real_free_share(&share); - else - { - DBUG_PRINT("share", - ("[%d] 0x%lx key: %s key_length: %d", - i, share, share->key, share->key_length)); - DBUG_PRINT("share", - ("db.tablename: %s.%s use_count: %d commit_count: %d", - share->db, share->table_name, - share->use_count, share->commit_count)); - } - } - pthread_mutex_unlock(&ndbcluster_mutex); - } + pthread_mutex_destroy(&injector_mutex); + pthread_cond_destroy(&injector_cond); + pthread_mutex_destroy(&ndb_schema_share_mutex); #endif - ndbcluster_util_inited= 0; + DBUG_RETURN(0); } @@ -674,19 +653,19 @@ void ndbcluster_binlog_init_handlerton() /* - check the availability af the apply_status share + check the availability af the ndb_apply_status share - return share, but do not increase refcount - return 0 if there is no share */ -static NDB_SHARE *ndbcluster_check_apply_status_share() +static NDB_SHARE *ndbcluster_check_ndb_apply_status_share() { pthread_mutex_lock(&ndbcluster_mutex); void *share= hash_search(&ndbcluster_open_tables, NDB_APPLY_TABLE_FILE, sizeof(NDB_APPLY_TABLE_FILE) - 1); - DBUG_PRINT("info",("ndbcluster_check_apply_status_share %s %p", - NDB_APPLY_TABLE_FILE, share)); + DBUG_PRINT("info",("ndbcluster_check_ndb_apply_status_share %s 0x%lx", + NDB_APPLY_TABLE_FILE, (long) share)); pthread_mutex_unlock(&ndbcluster_mutex); return (NDB_SHARE*) share; } @@ -696,25 +675,25 @@ static NDB_SHARE *ndbcluster_check_apply_status_share() - return share, but do not increase refcount - return 0 if there is no share */ -static NDB_SHARE *ndbcluster_check_schema_share() +static NDB_SHARE *ndbcluster_check_ndb_schema_share() { pthread_mutex_lock(&ndbcluster_mutex); void *share= hash_search(&ndbcluster_open_tables, NDB_SCHEMA_TABLE_FILE, sizeof(NDB_SCHEMA_TABLE_FILE) - 1); - DBUG_PRINT("info",("ndbcluster_check_schema_share %s %p", - NDB_SCHEMA_TABLE_FILE, share)); + DBUG_PRINT("info",("ndbcluster_check_ndb_schema_share %s 0x%lx", + NDB_SCHEMA_TABLE_FILE, (long) share)); pthread_mutex_unlock(&ndbcluster_mutex); return (NDB_SHARE*) share; } /* - Create the apply_status table + Create the ndb_apply_status table */ -static int ndbcluster_create_apply_status_table(THD *thd) +static int ndbcluster_create_ndb_apply_status_table(THD *thd) { - DBUG_ENTER("ndbcluster_create_apply_status_table"); + DBUG_ENTER("ndbcluster_create_ndb_apply_status_table"); /* Check if we already have the apply status table. @@ -722,7 +701,7 @@ static int ndbcluster_create_apply_status_table(THD *thd) and thus have a share */ - if (ndbcluster_check_apply_status_share()) + if (ndbcluster_check_ndb_apply_status_share()) DBUG_RETURN(0); if (g_ndb_cluster_connection->get_no_ready() <= 0) @@ -771,7 +750,7 @@ static int ndbcluster_create_schema_table(THD *thd) and thus have a share */ - if (ndbcluster_check_schema_share()) + if (ndbcluster_check_ndb_schema_share()) DBUG_RETURN(0); if (g_ndb_cluster_connection->get_no_ready() <= 0) @@ -815,30 +794,30 @@ static int ndbcluster_create_schema_table(THD *thd) int ndbcluster_setup_binlog_table_shares(THD *thd) { - if (!schema_share && - ndbcluster_check_schema_share() == 0) + if (!ndb_schema_share && + ndbcluster_check_ndb_schema_share() == 0) { pthread_mutex_lock(&LOCK_open); ndb_create_table_from_engine(thd, NDB_REP_DB, NDB_SCHEMA_TABLE); pthread_mutex_unlock(&LOCK_open); - if (!schema_share) + if (!ndb_schema_share) { ndbcluster_create_schema_table(thd); // always make sure we create the 'schema' first - if (!schema_share) + if (!ndb_schema_share) return 1; } } - if (!apply_status_share && - ndbcluster_check_apply_status_share() == 0) + if (!ndb_apply_status_share && + ndbcluster_check_ndb_apply_status_share() == 0) { pthread_mutex_lock(&LOCK_open); ndb_create_table_from_engine(thd, NDB_REP_DB, NDB_APPLY_TABLE); pthread_mutex_unlock(&LOCK_open); - if (!apply_status_share) + if (!ndb_apply_status_share) { - ndbcluster_create_apply_status_table(thd); - if (!apply_status_share) + ndbcluster_create_ndb_apply_status_table(thd); + if (!ndb_apply_status_share) return 1; } } @@ -1000,7 +979,7 @@ ndbcluster_update_slock(THD *thd, const char *table_name) { DBUG_ENTER("ndbcluster_update_slock"); - if (!schema_share) + if (!ndb_schema_share) { DBUG_RETURN(0); } @@ -1125,7 +1104,7 @@ ndbcluster_update_slock(THD *thd, ndb_error= this_error; break; } -end: + if (ndb_error) push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), @@ -1191,7 +1170,7 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share, DBUG_PRINT("enter", ("query: %s db: %s table_name: %s thd_ndb->options: %d", query, db, table_name, thd_ndb->options)); - if (!schema_share || thd_ndb->options & TNO_NO_LOG_SCHEMA_OP) + if (!ndb_schema_share || thd_ndb->options & TNO_NO_LOG_SCHEMA_OP) { DBUG_RETURN(0); } @@ -1265,10 +1244,20 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share, int no_storage_nodes= g_ndb_cluster_connection->no_db_nodes(); bitmap_init(&schema_subscribers, bitbuf, sizeof(bitbuf)*8, FALSE); bitmap_set_all(&schema_subscribers); - (void) pthread_mutex_lock(&schema_share->mutex); + + /* begin protect ndb_schema_share */ + pthread_mutex_lock(&ndb_schema_share_mutex); + if (ndb_schema_share == 0) + { + pthread_mutex_unlock(&ndb_schema_share_mutex); + if (ndb_schema_object) + ndb_free_schema_object(&ndb_schema_object, FALSE); + DBUG_RETURN(0); + } + (void) pthread_mutex_lock(&ndb_schema_share->mutex); for (i= 0; i < no_storage_nodes; i++) { - MY_BITMAP *table_subscribers= &schema_share->subscriber_bitmap[i]; + MY_BITMAP *table_subscribers= &ndb_schema_share->subscriber_bitmap[i]; if (!bitmap_is_clear_all(table_subscribers)) { bitmap_intersect(&schema_subscribers, @@ -1276,7 +1265,10 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share, updated= 1; } } - (void) pthread_mutex_unlock(&schema_share->mutex); + (void) pthread_mutex_unlock(&ndb_schema_share->mutex); + pthread_mutex_unlock(&ndb_schema_share_mutex); + /* end protect ndb_schema_share */ + if (updated) { bitmap_clear_bit(&schema_subscribers, node_id); @@ -1472,15 +1464,25 @@ end: &abstime); if (thd->killed) break; - (void) pthread_mutex_lock(&schema_share->mutex); + + /* begin protect ndb_schema_share */ + pthread_mutex_lock(&ndb_schema_share_mutex); + if (ndb_schema_share == 0) + { + pthread_mutex_unlock(&ndb_schema_share_mutex); + break; + } + (void) pthread_mutex_lock(&ndb_schema_share->mutex); for (i= 0; i < no_storage_nodes; i++) { /* remove any unsubscribed from schema_subscribers */ - MY_BITMAP *tmp= &schema_share->subscriber_bitmap[i]; + MY_BITMAP *tmp= &ndb_schema_share->subscriber_bitmap[i]; if (!bitmap_is_clear_all(tmp)) bitmap_intersect(&schema_subscribers, tmp); } - (void) pthread_mutex_unlock(&schema_share->mutex); + (void) pthread_mutex_unlock(&ndb_schema_share->mutex); + pthread_mutex_unlock(&ndb_schema_share_mutex); + /* end protect ndb_schema_share */ /* remove any unsubscribed from ndb_schema_object->slock */ bitmap_intersect(&ndb_schema_object->slock_bitmap, &schema_subscribers); @@ -1683,15 +1685,25 @@ ndb_handle_schema_change(THD *thd, Ndb *ndb, NdbEventOperation *pOp, (void) pthread_cond_signal(&injector_cond); pthread_mutex_lock(&ndbcluster_mutex); + /* ndb_share reference binlog free */ + DBUG_PRINT("NDB_SHARE", ("%s binlog free use_count: %u", + share->key, share->use_count)); free_share(&share, TRUE); if (is_remote_change && share && share->state != NSS_DROPPED) { DBUG_PRINT("info", ("remote change")); share->state= NSS_DROPPED; if (share->use_count != 1) + { + /* open handler holding reference */ + /* wait with freeing create ndb_share to below */ do_close_cached_tables= TRUE; + } else { + /* ndb_share reference create free */ + DBUG_PRINT("NDB_SHARE", ("%s create free use_count: %u", + share->key, share->use_count)); free_share(&share, TRUE); share= 0; } @@ -1714,6 +1726,9 @@ ndb_handle_schema_change(THD *thd, Ndb *ndb, NdbEventOperation *pOp, table_list.db= (char *)dbname; table_list.alias= table_list.table_name= (char *)tabname; close_cached_tables(thd, 0, &table_list); + /* ndb_share reference create free */ + DBUG_PRINT("NDB_SHARE", ("%s create free use_count: %u", + share->key, share->use_count)); free_share(&share); } DBUG_RETURN(0); @@ -1730,7 +1745,7 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, { DBUG_ENTER("ndb_binlog_thread_handle_schema_event"); NDB_SHARE *tmp_share= (NDB_SHARE *)pOp->getCustomData(); - if (tmp_share && schema_share == tmp_share) + if (tmp_share && ndb_schema_share == tmp_share) { NDBEVENT::TableEvent ev_type= pOp->getEventType(); DBUG_PRINT("enter", ("%s.%s ev_type: %d", @@ -1781,7 +1796,13 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, char key[FN_REFLEN]; build_table_filename(key, sizeof(key), schema->db, schema->name, "", 0); + /* ndb_share reference temporary, free below */ NDB_SHARE *share= get_share(key, 0, FALSE, FALSE); + if (share) + { + DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u", + share->key, share->use_count)); + } // invalidation already handled by binlog thread if (!share || !share->op) { @@ -1797,21 +1818,26 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, table_list.alias= table_list.table_name= schema->name; close_cached_tables(thd, 0, &table_list, FALSE); } + /* ndb_share reference temporary free */ if (share) + { + DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u", + share->key, share->use_count)); free_share(&share); + } } // fall through case SOT_CREATE_TABLE: pthread_mutex_lock(&LOCK_open); - if (ndbcluster_check_if_local_table(schema->db, schema->name)) - { - DBUG_PRINT("info", ("NDB binlog: Skipping locally defined table '%s.%s'", - schema->db, schema->name)); + if (ndbcluster_check_if_local_table(schema->db, schema->name)) + { + DBUG_PRINT("info", ("NDB binlog: Skipping locally defined table '%s.%s'", + schema->db, schema->name)); sql_print_error("NDB binlog: Skipping locally defined table '%s.%s' from " "binlog schema event '%s' from node %d. ", schema->db, schema->name, schema->query, schema->node_id); - } + } else if (ndb_create_table_from_engine(thd, schema->db, schema->name)) { sql_print_error("NDB binlog: Could not discover table '%s.%s' from " @@ -1828,14 +1854,27 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, log_query= 1; break; case SOT_DROP_DB: - run_query(thd, schema->query, - schema->query + schema->query_length, - TRUE, /* print error */ - TRUE); /* don't binlog the query */ - /* binlog dropping database after any table operations */ - post_epoch_log_list->push_back(schema, mem_root); - /* acknowledge this query _after_ epoch completion */ - post_epoch_unlock= 1; + /* Drop the database locally if it only contains ndb tables */ + if (! ndbcluster_check_if_local_tables_in_db(thd, schema->db)) + { + run_query(thd, schema->query, + schema->query + schema->query_length, + TRUE, /* print error */ + TRUE); /* don't binlog the query */ + /* binlog dropping database after any table operations */ + post_epoch_log_list->push_back(schema, mem_root); + /* acknowledge this query _after_ epoch completion */ + post_epoch_unlock= 1; + } + else + { + /* Database contained local tables, leave it */ + sql_print_error("NDB binlog: Skipping drop database '%s' since it contained local tables " + "binlog schema event '%s' from node %d. ", + schema->db, schema->query, + schema->node_id); + log_query= 1; + } break; case SOT_CREATE_DB: /* fall through */ @@ -1884,15 +1923,25 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, case NDBEVENT::TE_CLUSTER_FAILURE: if (ndb_extra_logging) sql_print_information("NDB Binlog: cluster failure for %s at epoch %u.", - schema_share->key, (unsigned) pOp->getGCI()); + ndb_schema_share->key, (unsigned) pOp->getGCI()); // fall through case NDBEVENT::TE_DROP: if (ndb_extra_logging && ndb_binlog_tables_inited && ndb_binlog_running) sql_print_information("NDB Binlog: ndb tables initially " "read only on reconnect."); - free_share(&schema_share); - schema_share= 0; + + /* begin protect ndb_schema_share */ + pthread_mutex_lock(&ndb_schema_share_mutex); + /* ndb_share reference binlog extra free */ + DBUG_PRINT("NDB_SHARE", ("%s binlog extra free use_count: %u", + ndb_schema_share->key, + ndb_schema_share->use_count)); + free_share(&ndb_schema_share); + ndb_schema_share= 0; + pthread_mutex_unlock(&ndb_schema_share_mutex); + /* end protect ndb_schema_share */ + close_cached_tables((THD*) 0, 0, (TABLE_LIST*) 0, FALSE); // fall through case NDBEVENT::TE_ALTER: @@ -2014,7 +2063,13 @@ ndb_binlog_thread_handle_schema_event_post_epoch(THD *thd, pthread_mutex_unlock(&ndbcluster_mutex); continue; } + /* ndb_share reference temporary, free below */ NDB_SHARE *share= get_share(key, 0, FALSE, FALSE); + if (share) + { + DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u", + share->key, share->use_count)); + } switch (schema_type) { case SOT_DROP_DB: @@ -2059,22 +2114,25 @@ ndb_binlog_thread_handle_schema_event_post_epoch(THD *thd, */ if (share) { + /* ndb_share reference temporary free */ + DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u", + share->key, share->use_count)); free_share(&share); share= 0; } pthread_mutex_lock(&LOCK_open); - if (ndbcluster_check_if_local_table(schema->db, schema->name)) - { - DBUG_PRINT("info", ("NDB binlog: Skipping locally defined table '%s.%s'", - schema->db, schema->name)); + if (ndbcluster_check_if_local_table(schema->db, schema->name)) + { + DBUG_PRINT("info", ("NDB binlog: Skipping locally defined table '%s.%s'", + schema->db, schema->name)); sql_print_error("NDB binlog: Skipping locally defined table '%s.%s' from " "binlog schema event '%s' from node %d. ", schema->db, schema->name, schema->query, schema->node_id); - } + } else if (ndb_create_table_from_engine(thd, schema->db, schema->name)) - { - sql_print_error("NDB binlog: Could not discover table '%s.%s' from " + { + sql_print_error("NDB binlog: Could not discover table '%s.%s' from " "binlog schema event '%s' from node %d. my_errno: %d", schema->db, schema->name, schema->query, schema->node_id, my_errno); @@ -2091,6 +2149,9 @@ ndb_binlog_thread_handle_schema_event_post_epoch(THD *thd, } if (share) { + /* ndb_share reference temporary free */ + DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u", + share->key, share->use_count)); free_share(&share); share= 0; } @@ -2118,15 +2179,15 @@ ndb_binlog_thread_handle_schema_event_post_epoch(THD *thd, /********************************************************************* Internal helper functions for handeling of the cluster replication tables - - binlog_index - - apply_status + - ndb_binlog_index + - ndb_apply_status *********************************************************************/ /* struct to hold the data to be inserted into the - binlog_index table + ndb_binlog_index table */ -struct Binlog_index_row { +struct ndb_binlog_index_row { ulonglong gci; const char *master_log_file; ulonglong master_log_pos; @@ -2137,10 +2198,10 @@ struct Binlog_index_row { }; /* - Open the binlog_index table + Open the ndb_binlog_index table */ -static int open_binlog_index(THD *thd, TABLE_LIST *tables, - TABLE **binlog_index) +static int open_ndb_binlog_index(THD *thd, TABLE_LIST *tables, + TABLE **ndb_binlog_index) { static char repdb[]= NDB_REP_DB; static char reptable[]= NDB_REP_TABLE; @@ -2156,26 +2217,26 @@ static int open_binlog_index(THD *thd, TABLE_LIST *tables, thd->clear_error(); if (open_tables(thd, &tables, &counter, MYSQL_LOCK_IGNORE_FLUSH)) { - sql_print_error("NDB Binlog: Opening binlog_index: %d, '%s'", + sql_print_error("NDB Binlog: Opening ndb_binlog_index: %d, '%s'", thd->net.last_errno, thd->net.last_error ? thd->net.last_error : ""); thd->proc_info= save_proc_info; return -1; } - *binlog_index= tables->table; + *ndb_binlog_index= tables->table; thd->proc_info= save_proc_info; - (*binlog_index)->use_all_columns(); + (*ndb_binlog_index)->use_all_columns(); return 0; } /* - Insert one row in the binlog_index + Insert one row in the ndb_binlog_index */ -int ndb_add_binlog_index(THD *thd, void *_row) +int ndb_add_ndb_binlog_index(THD *thd, void *_row) { - Binlog_index_row &row= *(Binlog_index_row *) _row; + ndb_binlog_index_row &row= *(ndb_binlog_index_row *) _row; int error= 0; bool need_reopen; /* @@ -2187,10 +2248,10 @@ int ndb_add_binlog_index(THD *thd, void *_row) for ( ; ; ) /* loop for need_reopen */ { - if (!binlog_index && open_binlog_index(thd, &binlog_tables, &binlog_index)) + if (!ndb_binlog_index && open_ndb_binlog_index(thd, &binlog_tables, &ndb_binlog_index)) { error= -1; - goto add_binlog_index_err; + goto add_ndb_binlog_index_err; } if (lock_tables(thd, &binlog_tables, 1, &need_reopen)) @@ -2199,43 +2260,43 @@ int ndb_add_binlog_index(THD *thd, void *_row) { TABLE_LIST *p_binlog_tables= &binlog_tables; close_tables_for_reopen(thd, &p_binlog_tables); - binlog_index= 0; + ndb_binlog_index= 0; continue; } - sql_print_error("NDB Binlog: Unable to lock table binlog_index"); + sql_print_error("NDB Binlog: Unable to lock table ndb_binlog_index"); error= -1; - goto add_binlog_index_err; + goto add_ndb_binlog_index_err; } break; } // Set all fields non-null. - if(binlog_index->s->null_bytes > 0) - bzero(binlog_index->record[0], binlog_index->s->null_bytes); - binlog_index->field[0]->store(row.master_log_pos); - binlog_index->field[1]->store(row.master_log_file, + if(ndb_binlog_index->s->null_bytes > 0) + bzero(ndb_binlog_index->record[0], ndb_binlog_index->s->null_bytes); + ndb_binlog_index->field[0]->store(row.master_log_pos); + ndb_binlog_index->field[1]->store(row.master_log_file, strlen(row.master_log_file), &my_charset_bin); - binlog_index->field[2]->store(row.gci); - binlog_index->field[3]->store(row.n_inserts); - binlog_index->field[4]->store(row.n_updates); - binlog_index->field[5]->store(row.n_deletes); - binlog_index->field[6]->store(row.n_schemaops); + ndb_binlog_index->field[2]->store(row.gci); + ndb_binlog_index->field[3]->store(row.n_inserts); + ndb_binlog_index->field[4]->store(row.n_updates); + ndb_binlog_index->field[5]->store(row.n_deletes); + ndb_binlog_index->field[6]->store(row.n_schemaops); - if ((error= binlog_index->file->ha_write_row(binlog_index->record[0]))) + if ((error= ndb_binlog_index->file->ha_write_row(ndb_binlog_index->record[0]))) { - sql_print_error("NDB Binlog: Writing row to binlog_index: %d", error); + sql_print_error("NDB Binlog: Writing row to ndb_binlog_index: %d", error); error= -1; - goto add_binlog_index_err; + goto add_ndb_binlog_index_err; } mysql_unlock_tables(thd, thd->lock); thd->lock= 0; thd->options= saved_options; return 0; -add_binlog_index_err: +add_ndb_binlog_index_err: close_thread_tables(thd); - binlog_index= 0; + ndb_binlog_index= 0; thd->options= saved_options; return error; } @@ -2259,6 +2320,7 @@ int ndbcluster_binlog_start() pthread_mutex_init(&injector_mutex, MY_MUTEX_INIT_FAST); pthread_cond_init(&injector_cond, NULL); + pthread_mutex_init(&ndb_schema_share_mutex, MY_MUTEX_INIT_FAST); /* Create injector thread */ if (pthread_create(&ndb_binlog_thread, &connection_attrib, @@ -2270,31 +2332,20 @@ int ndbcluster_binlog_start() DBUG_RETURN(-1); } - /* - Wait for the ndb injector thread to finish starting up. - */ + ndbcluster_binlog_inited= 1; + + /* Wait for the injector thread to start */ pthread_mutex_lock(&injector_mutex); while (!ndb_binlog_thread_running) pthread_cond_wait(&injector_cond, &injector_mutex); pthread_mutex_unlock(&injector_mutex); - + if (ndb_binlog_thread_running < 0) DBUG_RETURN(-1); DBUG_RETURN(0); } -static void ndbcluster_binlog_close_connection(THD *thd) -{ - DBUG_ENTER("ndbcluster_binlog_close_connection"); - const char *save_info= thd->proc_info; - thd->proc_info= "ndbcluster_binlog_close_connection"; - do_ndbcluster_binlog_close_connection= BCCC_exit; - while (ndb_binlog_thread_running > 0) - sleep(1); - thd->proc_info= save_info; - DBUG_VOID_RETURN; -} /************************************************************** Internal helper functions for creating/dropping ndb events @@ -2334,6 +2385,32 @@ ndbcluster_check_if_local_table(const char *dbname, const char *tabname) DBUG_RETURN(false); } +bool +ndbcluster_check_if_local_tables_in_db(THD *thd, const char *dbname) +{ + DBUG_ENTER("ndbcluster_check_if_local_tables_in_db"); + DBUG_PRINT("info", ("Looking for files in directory %s", dbname)); + char *tabname; + List<char> files; + char path[FN_REFLEN]; + + build_table_filename(path, sizeof(path), dbname, "", "", 0); + if (find_files(thd, &files, dbname, path, NullS, 0) != FIND_FILES_OK) + { + DBUG_PRINT("info", ("Failed to find files")); + DBUG_RETURN(true); + } + DBUG_PRINT("info",("found: %d files", files.elements)); + while ((tabname= files.pop())) + { + DBUG_PRINT("info", ("Found table %s", tabname)); + if (ndbcluster_check_if_local_table(dbname, tabname)) + DBUG_RETURN(true); + } + + DBUG_RETURN(false); +} + /* Common function for setting up everything for logging a table at create/discover. @@ -2376,20 +2453,42 @@ int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key, pthread_mutex_unlock(&ndbcluster_mutex); DBUG_RETURN(1); } - handle_trailing_share(share); + if (!share_may_exist || share->connect_count != + g_ndb_cluster_connection->get_connect_count()) + { + handle_trailing_share(share); + share= NULL; + } } /* Create share which is needed to hold replication information */ - if (!(share= get_share(key, 0, TRUE, TRUE))) + if (share) + { + /* ndb_share reference create */ + ++share->use_count; + DBUG_PRINT("NDB_SHARE", ("%s create use_count: %u", + share->key, share->use_count)); + } + /* ndb_share reference create */ + else if (!(share= get_share(key, 0, TRUE, TRUE))) { sql_print_error("NDB Binlog: " "allocating table share for %s failed", key); } + else + { + DBUG_PRINT("NDB_SHARE", ("%s create use_count: %u", + share->key, share->use_count)); + } - if (!schema_share && + if (!ndb_schema_share && strcmp(share->db, NDB_REP_DB) == 0 && strcmp(share->table_name, NDB_SCHEMA_TABLE) == 0) do_event_op= 1; + else if (!ndb_apply_status_share && + strcmp(share->db, NDB_REP_DB) == 0 && + strcmp(share->table_name, NDB_APPLY_TABLE) == 0) + do_event_op= 1; if (!do_event_op) { @@ -2517,7 +2616,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab, } else { - if (schema_share || strcmp(share->db, NDB_REP_DB) || + if (ndb_schema_share || strcmp(share->db, NDB_REP_DB) || strcmp(share->table_name, NDB_SCHEMA_TABLE)) { my_event.setReport(NDBEVENT::ER_UPDATED); @@ -2654,14 +2753,14 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab, DBUG_RETURN(0); } - int do_schema_share= 0, do_apply_status_share= 0; - if (!schema_share && strcmp(share->db, NDB_REP_DB) == 0 && + int do_ndb_schema_share= 0, do_ndb_apply_status_share= 0; + if (!ndb_schema_share && strcmp(share->db, NDB_REP_DB) == 0 && strcmp(share->table_name, NDB_SCHEMA_TABLE) == 0) - do_schema_share= 1; - else if (!apply_status_share && strcmp(share->db, NDB_REP_DB) == 0 && + do_ndb_schema_share= 1; + else if (!ndb_apply_status_share && strcmp(share->db, NDB_REP_DB) == 0 && strcmp(share->table_name, NDB_APPLY_TABLE) == 0) - do_apply_status_share= 1; - else if (!binlog_filter->db_ok(share->db)) + do_ndb_apply_status_share= 1; + else if (!binlog_filter->db_ok(share->db) || !ndb_binlog_running) { share->flags|= NSF_NO_BINLOG; DBUG_RETURN(0); @@ -2673,6 +2772,9 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab, DBUG_ASSERT(share->use_count > 1); sql_print_error("NDB Binlog: discover reusing old ev op"); + /* ndb_share reference ToDo free */ + DBUG_PRINT("NDB_SHARE", ("%s ToDo free use_count: %u", + share->key, share->use_count)); free_share(&share); // old event op already has reference DBUG_RETURN(0); } @@ -2684,7 +2786,7 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab, { pthread_mutex_lock(&injector_mutex); Ndb *ndb= injector_ndb; - if (do_schema_share) + if (do_ndb_schema_share) ndb= schema_ndb; if (ndb == 0) @@ -2694,7 +2796,7 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab, } NdbEventOperation* op; - if (do_schema_share) + if (do_ndb_schema_share) op= ndb->createEventOperation(event_name); else { @@ -2721,10 +2823,9 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab, if (share->flags & NSF_BLOB_FLAG) op->mergeEvents(TRUE); // currently not inherited from event - DBUG_PRINT("info", ("share->ndb_value[0]: 0x%x", - share->ndb_value[0])); - DBUG_PRINT("info", ("share->ndb_value[1]: 0x%x", - share->ndb_value[1])); + DBUG_PRINT("info", ("share->ndb_value[0]: 0x%lx share->ndb_value[1]: 0x%lx", + (long) share->ndb_value[0], + (long) share->ndb_value[1])); int n_columns= ndbtab->getNoOfColumns(); int n_fields= table ? table->s->fields : 0; // XXX ??? for (int j= 0; j < n_columns; j++) @@ -2778,12 +2879,14 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab, } share->ndb_value[0][j].ptr= attr0.ptr; share->ndb_value[1][j].ptr= attr1.ptr; - DBUG_PRINT("info", ("&share->ndb_value[0][%d]: 0x%x " - "share->ndb_value[0][%d]: 0x%x", - j, &share->ndb_value[0][j], j, attr0.ptr)); - DBUG_PRINT("info", ("&share->ndb_value[1][%d]: 0x%x " - "share->ndb_value[1][%d]: 0x%x", - j, &share->ndb_value[0][j], j, attr1.ptr)); + DBUG_PRINT("info", ("&share->ndb_value[0][%d]: 0x%lx " + "share->ndb_value[0][%d]: 0x%lx", + j, (long) &share->ndb_value[0][j], + j, (long) attr0.ptr)); + DBUG_PRINT("info", ("&share->ndb_value[1][%d]: 0x%lx " + "share->ndb_value[1][%d]: 0x%lx", + j, (long) &share->ndb_value[0][j], + j, (long) attr1.ptr)); } op->setCustomData((void *) share); // set before execute share->op= op; // assign op in NDB_SHARE @@ -2814,20 +2917,29 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab, break; } + /* ndb_share reference binlog */ get_share(share); - if (do_apply_status_share) + DBUG_PRINT("NDB_SHARE", ("%s binlog use_count: %u", + share->key, share->use_count)); + if (do_ndb_apply_status_share) { - apply_status_share= get_share(share); + /* ndb_share reference binlog extra */ + ndb_apply_status_share= get_share(share); + DBUG_PRINT("NDB_SHARE", ("%s binlog extra use_count: %u", + share->key, share->use_count)); (void) pthread_cond_signal(&injector_cond); } - else if (do_schema_share) + else if (do_ndb_schema_share) { - schema_share= get_share(share); + /* ndb_share reference binlog extra */ + ndb_schema_share= get_share(share); + DBUG_PRINT("NDB_SHARE", ("%s binlog extra use_count: %u", + share->key, share->use_count)); (void) pthread_cond_signal(&injector_cond); } - DBUG_PRINT("info",("%s share->op: 0x%lx, share->use_count: %u", - share->key, share->op, share->use_count)); + DBUG_PRINT("info",("%s share->op: 0x%lx share->use_count: %u", + share->key, (long) share->op, share->use_count)); if (ndb_extra_logging) sql_print_information("NDB Binlog: logging %s", share->key); @@ -2945,7 +3057,7 @@ ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name, Handle error states on events from the storage nodes */ static int ndb_binlog_thread_handle_error(Ndb *ndb, NdbEventOperation *pOp, - Binlog_index_row &row) + ndb_binlog_index_row &row) { NDB_SHARE *share= (NDB_SHARE *)pOp->getCustomData(); DBUG_ENTER("ndb_binlog_thread_handle_error"); @@ -2954,7 +3066,7 @@ static int ndb_binlog_thread_handle_error(Ndb *ndb, NdbEventOperation *pOp, if (overrun) { /* - ToDo: this error should rather clear the binlog_index... + ToDo: this error should rather clear the ndb_binlog_index... and continue */ sql_print_error("NDB Binlog: Overrun in event buffer, " @@ -2967,7 +3079,7 @@ static int ndb_binlog_thread_handle_error(Ndb *ndb, NdbEventOperation *pOp, if (!pOp->isConsistent()) { /* - ToDo: this error should rather clear the binlog_index... + ToDo: this error should rather clear the ndb_binlog_index... and continue */ sql_print_error("NDB Binlog: Not Consistent. Cannot " @@ -2987,7 +3099,7 @@ static int ndb_binlog_thread_handle_error(Ndb *ndb, NdbEventOperation *pOp, static int ndb_binlog_thread_handle_non_data_event(THD *thd, Ndb *ndb, NdbEventOperation *pOp, - Binlog_index_row &row) + ndb_binlog_index_row &row) { NDB_SHARE *share= (NDB_SHARE *)pOp->getCustomData(); NDBEVENT::TableEvent type= pOp->getEventType(); @@ -3003,29 +3115,36 @@ ndb_binlog_thread_handle_non_data_event(THD *thd, Ndb *ndb, if (ndb_extra_logging) sql_print_information("NDB Binlog: cluster failure for %s at epoch %u.", share->key, (unsigned) pOp->getGCI()); - if (apply_status_share == share) + if (ndb_apply_status_share == share) { if (ndb_extra_logging && ndb_binlog_tables_inited && ndb_binlog_running) sql_print_information("NDB Binlog: ndb tables initially " "read only on reconnect."); - free_share(&apply_status_share); - apply_status_share= 0; + /* ndb_share reference binlog extra free */ + DBUG_PRINT("NDB_SHARE", ("%s binlog extra free use_count: %u", + share->key, share->use_count)); + free_share(&ndb_apply_status_share); + ndb_apply_status_share= 0; } - DBUG_PRINT("info", ("CLUSTER FAILURE EVENT: " - "%s received share: 0x%lx op: %lx share op: %lx " - "op_old: %lx", - share->key, share, pOp, share->op, share->op_old)); + DBUG_PRINT("error", ("CLUSTER FAILURE EVENT: " + "%s received share: 0x%lx op: 0x%lx share op: 0x%lx " + "op_old: 0x%lx", + share->key, (long) share, (long) pOp, + (long) share->op, (long) share->op_old)); break; case NDBEVENT::TE_DROP: - if (apply_status_share == share) + if (ndb_apply_status_share == share) { if (ndb_extra_logging && ndb_binlog_tables_inited && ndb_binlog_running) sql_print_information("NDB Binlog: ndb tables initially " "read only on reconnect."); - free_share(&apply_status_share); - apply_status_share= 0; + /* ndb_share reference binlog extra free */ + DBUG_PRINT("NDB_SHARE", ("%s binlog extra free use_count: %u", + share->key, share->use_count)); + free_share(&ndb_apply_status_share); + ndb_apply_status_share= 0; } /* ToDo: remove printout */ if (ndb_extra_logging) @@ -3033,10 +3152,11 @@ ndb_binlog_thread_handle_non_data_event(THD *thd, Ndb *ndb, // fall through case NDBEVENT::TE_ALTER: row.n_schemaops++; - DBUG_PRINT("info", ("TABLE %s EVENT: %s received share: 0x%lx op: %lx " - "share op: %lx op_old: %lx", - type == NDBEVENT::TE_DROP ? "DROP" : "ALTER", - share->key, share, pOp, share->op, share->op_old)); + DBUG_PRINT("info", ("TABLE %s EVENT: %s received share: 0x%lx op: 0x%lx " + "share op: 0x%lx op_old: 0x%lx", + type == NDBEVENT::TE_DROP ? "DROP" : "ALTER", + share->key, (long) share, (long) pOp, + (long) share->op, (long) share->op_old)); break; case NDBEVENT::TE_NODE_FAILURE: /* fall through */ @@ -3060,11 +3180,11 @@ ndb_binlog_thread_handle_non_data_event(THD *thd, Ndb *ndb, */ static int ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, - Binlog_index_row &row, + ndb_binlog_index_row &row, injector::transaction &trans) { NDB_SHARE *share= (NDB_SHARE*) pOp->getCustomData(); - if (share == apply_status_share) + if (share == ndb_apply_status_share) return 0; TABLE *table= share->table; @@ -3105,15 +3225,17 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, if (share->flags & NSF_BLOB_FLAG) { my_ptrdiff_t ptrdiff= 0; - int ret= get_ndb_blobs_value(table, share->ndb_value[0], - blobs_buffer[0], blobs_buffer_size[0], - ptrdiff); + IF_DBUG(int ret =) get_ndb_blobs_value(table, share->ndb_value[0], + blobs_buffer[0], + blobs_buffer_size[0], + ptrdiff); DBUG_ASSERT(ret == 0); } ndb_unpack_record(table, share->ndb_value[0], &b, table->record[0]); - int ret= trans.write_row(::server_id, - injector::transaction::table(table, TRUE), - &b, n_fields, table->record[0]); + IF_DBUG(int ret=) trans.write_row(::server_id, + injector::transaction::table(table, + TRUE), + &b, n_fields, table->record[0]); DBUG_ASSERT(ret == 0); } break; @@ -3131,27 +3253,29 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, n= 0; /* use the primary key only as it save time and space and it is the only thing needed to log the delete - */ + */ else n= 1; /* we use the before values since we don't have a primary key since the mysql server does not handle the hidden primary key - */ + */ if (share->flags & NSF_BLOB_FLAG) { my_ptrdiff_t ptrdiff= table->record[n] - table->record[0]; - int ret= get_ndb_blobs_value(table, share->ndb_value[n], - blobs_buffer[n], blobs_buffer_size[n], - ptrdiff); + IF_DBUG(int ret =) get_ndb_blobs_value(table, share->ndb_value[n], + blobs_buffer[n], + blobs_buffer_size[n], + ptrdiff); DBUG_ASSERT(ret == 0); } ndb_unpack_record(table, share->ndb_value[n], &b, table->record[n]); DBUG_EXECUTE("info", print_records(table, table->record[n]);); - int ret= trans.delete_row(::server_id, - injector::transaction::table(table, TRUE), - &b, n_fields, table->record[n]); + IF_DBUG(int ret =) trans.delete_row(::server_id, + injector::transaction::table(table, + TRUE), + &b, n_fields, table->record[n]); DBUG_ASSERT(ret == 0); } break; @@ -3163,9 +3287,10 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, if (share->flags & NSF_BLOB_FLAG) { my_ptrdiff_t ptrdiff= 0; - int ret= get_ndb_blobs_value(table, share->ndb_value[0], - blobs_buffer[0], blobs_buffer_size[0], - ptrdiff); + IF_DBUG(int ret =) get_ndb_blobs_value(table, share->ndb_value[0], + blobs_buffer[0], + blobs_buffer_size[0], + ptrdiff); DBUG_ASSERT(ret == 0); } ndb_unpack_record(table, share->ndb_value[0], @@ -3176,7 +3301,7 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, /* since table has a primary key, we can do a write using only after values - */ + */ trans.write_row(::server_id, injector::transaction::table(table, TRUE), &b, n_fields, table->record[0]);// after values } @@ -3185,22 +3310,24 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, /* mysql server cannot handle the ndb hidden key and therefore needs the before image as well - */ + */ if (share->flags & NSF_BLOB_FLAG) { my_ptrdiff_t ptrdiff= table->record[1] - table->record[0]; - int ret= get_ndb_blobs_value(table, share->ndb_value[1], - blobs_buffer[1], blobs_buffer_size[1], - ptrdiff); + IF_DBUG(int ret =) get_ndb_blobs_value(table, share->ndb_value[1], + blobs_buffer[1], + blobs_buffer_size[1], + ptrdiff); DBUG_ASSERT(ret == 0); } ndb_unpack_record(table, share->ndb_value[1], &b, table->record[1]); DBUG_EXECUTE("info", print_records(table, table->record[1]);); - int ret= trans.update_row(::server_id, - injector::transaction::table(table, TRUE), - &b, n_fields, - table->record[1], // before values - table->record[0]);// after values + IF_DBUG(int ret =) trans.update_row(::server_id, + injector::transaction::table(table, + TRUE), + &b, n_fields, + table->record[1], // before values + table->record[0]);// after values DBUG_ASSERT(ret == 0); } } @@ -3335,7 +3462,7 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) Ndb *i_ndb= 0; Ndb *s_ndb= 0; Thd_ndb *thd_ndb=0; - int ndb_update_binlog_index= 1; + int ndb_update_ndb_binlog_index= 1; injector *inj= injector::instance(); #ifdef RUN_NDB_BINLOG_TIMER @@ -3376,7 +3503,6 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) thd->command= COM_DAEMON; thd->system_thread= SYSTEM_THREAD_NDBCLUSTER_BINLOG; thd->version= refresh_version; - thd->set_time(); thd->main_security_ctx.host_or_ip= ""; thd->client_capabilities= 0; my_net_init(&thd->net, 0); @@ -3399,6 +3525,9 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) s_ndb->init()) { sql_print_error("NDB Binlog: Getting Schema Ndb object failed"); + ndb_binlog_thread_running= -1; + pthread_mutex_unlock(&injector_mutex); + pthread_cond_signal(&injector_cond); goto err; } @@ -3429,24 +3558,22 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) p_latest_trans_gci= injector_ndb->get_ndb_cluster_connection().get_latest_trans_gci(); schema_ndb= s_ndb; - ndb_binlog_thread_running= 1; + if (opt_bin_log) { if (global_system_variables.binlog_format == BINLOG_FORMAT_ROW || global_system_variables.binlog_format == BINLOG_FORMAT_MIXED) { ndb_binlog_running= TRUE; - thd->current_stmt_binlog_row_based= TRUE; // If in mixed mode } else { sql_print_error("NDB: only row based binary logging is supported"); } } - /* - We signal the thread that started us that we've finished - starting up. - */ + + /* Thread start up completed */ + ndb_binlog_thread_running= 1; pthread_mutex_unlock(&injector_mutex); pthread_cond_signal(&injector_cond); @@ -3458,14 +3585,14 @@ restart: thd->proc_info= "Waiting for ndbcluster to start"; pthread_mutex_lock(&injector_mutex); - while (!schema_share || - (ndb_binlog_running && !apply_status_share)) + while (!ndb_schema_share || + (ndb_binlog_running && !ndb_apply_status_share)) { /* ndb not connected yet */ struct timespec abstime; set_timespec(abstime, 1); pthread_cond_timedwait(&injector_cond, &injector_mutex, &abstime); - if (abort_loop) + if (ndbcluster_binlog_terminating) { pthread_mutex_unlock(&injector_mutex); goto err; @@ -3490,21 +3617,23 @@ restart: { // wait for the first event thd->proc_info= "Waiting for first event from ndbcluster"; - DBUG_PRINT("info", ("Waiting for the first event")); int schema_res, res; Uint64 schema_gci; do { - if (abort_loop) + DBUG_PRINT("info", ("Waiting for the first event")); + + if (ndbcluster_binlog_terminating) goto err; + schema_res= s_ndb->pollEvents(100, &schema_gci); - } while (ndb_latest_received_binlog_epoch == schema_gci); + } while (schema_gci == 0 || ndb_latest_received_binlog_epoch == schema_gci); if (ndb_binlog_running) { Uint64 gci= i_ndb->getLatestGCI(); while (gci < schema_gci || gci == ndb_latest_received_binlog_epoch) { - if (abort_loop) + if (ndbcluster_binlog_terminating) goto err; res= i_ndb->pollEvents(10, &gci); } @@ -3514,7 +3643,8 @@ restart: } } // now check that we have epochs consistant with what we had before the restart - DBUG_PRINT("info", ("schema_res: %d schema_gci: %d", schema_res, schema_gci)); + DBUG_PRINT("info", ("schema_res: %d schema_gci: %lu", schema_res, + (long) schema_gci)); { i_ndb->flushIncompleteEvents(schema_gci); s_ndb->flushIncompleteEvents(schema_gci); @@ -3546,11 +3676,12 @@ restart: static char db[]= ""; thd->db= db; if (ndb_binlog_running) - open_binlog_index(thd, &binlog_tables, &binlog_index); + open_ndb_binlog_index(thd, &binlog_tables, &ndb_binlog_index); thd->db= db; } do_ndbcluster_binlog_close_connection= BCCC_running; - for ( ; !((abort_loop || do_ndbcluster_binlog_close_connection) && + for ( ; !((ndbcluster_binlog_terminating || + do_ndbcluster_binlog_close_connection) && ndb_latest_handled_binlog_epoch >= *p_latest_trans_gci) && do_ndbcluster_binlog_close_connection != BCCC_restart; ) { @@ -3558,9 +3689,11 @@ restart: if (do_ndbcluster_binlog_close_connection) { DBUG_PRINT("info", ("do_ndbcluster_binlog_close_connection: %d, " - "ndb_latest_handled_binlog_epoch: %llu, " - "*p_latest_trans_gci: %llu", do_ndbcluster_binlog_close_connection, - ndb_latest_handled_binlog_epoch, *p_latest_trans_gci)); + "ndb_latest_handled_binlog_epoch: %lu, " + "*p_latest_trans_gci: %lu", + do_ndbcluster_binlog_close_connection, + (ulong) ndb_latest_handled_binlog_epoch, + (ulong) *p_latest_trans_gci)); } #endif #ifdef RUN_NDB_BINLOG_TIMER @@ -3595,17 +3728,18 @@ restart: schema_res= s_ndb->pollEvents(10, &schema_gci); } - if ((abort_loop || do_ndbcluster_binlog_close_connection) && + if ((ndbcluster_binlog_terminating || + do_ndbcluster_binlog_close_connection) && (ndb_latest_handled_binlog_epoch >= *p_latest_trans_gci || !ndb_binlog_running)) break; /* Shutting down server */ - if (binlog_index && binlog_index->s->version < refresh_version) + if (ndb_binlog_index && ndb_binlog_index->s->version < refresh_version) { - if (binlog_index->s->version < refresh_version) + if (ndb_binlog_index->s->version < refresh_version) { close_thread_tables(thd); - binlog_index= 0; + ndb_binlog_index= 0; } } @@ -3648,9 +3782,10 @@ restart: do_ndbcluster_binlog_close_connection= BCCC_restart; if (ndb_latest_received_binlog_epoch < *p_latest_trans_gci && ndb_binlog_running) { - sql_print_error("NDB Binlog: latest transaction in epoch %lld not in binlog " - "as latest received epoch is %lld", - *p_latest_trans_gci, ndb_latest_received_binlog_epoch); + sql_print_error("NDB Binlog: latest transaction in epoch %lu not in binlog " + "as latest received epoch is %lu", + (ulong) *p_latest_trans_gci, + (ulong) ndb_latest_received_binlog_epoch); } } } @@ -3668,7 +3803,7 @@ restart: DBUG_PRINT("info", ("pollEvents res: %d", res)); thd->proc_info= "Processing events"; NdbEventOperation *pOp= i_ndb->nextEvent(); - Binlog_index_row row; + ndb_binlog_index_row row; while (pOp != NULL) { #ifdef RUN_NDB_BINLOG_TIMER @@ -3698,8 +3833,8 @@ restart: != NULL) { NDB_SHARE *share= (NDB_SHARE*)gci_op->getCustomData(); - DBUG_PRINT("info", ("per gci_op: %p share: %p event_types: 0x%x", - gci_op, share, event_types)); + DBUG_PRINT("info", ("per gci_op: 0x%lx share: 0x%lx event_types: 0x%x", + (long) gci_op, (long) share, event_types)); // workaround for interface returning TE_STOP events // which are normally filtered out below in the nextEvent loop if ((event_types & ~NdbDictionary::Event::TE_STOP) == 0) @@ -3715,13 +3850,15 @@ restart: gci_op->getEvent()->getTable()->getName())); continue; } - if (share == apply_status_share) + if (share == ndb_apply_status_share) { // skip this table, it is handled specially continue; } TABLE *table= share->table; +#ifndef DBUG_OFF const LEX_STRING &name= table->s->table_name; +#endif if ((event_types & (NdbDictionary::Event::TE_INSERT | NdbDictionary::Event::TE_UPDATE | NdbDictionary::Event::TE_DELETE)) == 0) @@ -3738,20 +3875,22 @@ restart: } DBUG_PRINT("info", ("use_table: %.*s", name.length, name.str)); injector::transaction::table tbl(table, TRUE); - int ret= trans.use_table(::server_id, tbl); + IF_DBUG(int ret=) trans.use_table(::server_id, tbl); DBUG_ASSERT(ret == 0); } } if (trans.good()) { - if (apply_status_share) + if (ndb_apply_status_share) { - TABLE *table= apply_status_share->table; + TABLE *table= ndb_apply_status_share->table; - const LEX_STRING& name=table->s->table_name; +#ifndef DBUG_OFF + const LEX_STRING& name= table->s->table_name; DBUG_PRINT("info", ("use_table: %.*s", name.length, name.str)); +#endif injector::transaction::table tbl(table, TRUE); - int ret= trans.use_table(::server_id, tbl); + IF_DBUG(int ret=) trans.use_table(::server_id, tbl); DBUG_ASSERT(ret == 0); // Set all fields non-null. @@ -3785,11 +3924,13 @@ restart: { NDB_SHARE *share= (NDB_SHARE*) pOp->getCustomData(); DBUG_PRINT("info", - ("EVENT TYPE: %d GCI: %lld last applied: %lld " - "share: 0x%lx (%s.%s)", pOp->getEventType(), gci, - ndb_latest_applied_binlog_epoch, share, - share ? share->db : "share == NULL", - share ? share->table_name : "")); + ("EVENT TYPE: %d GCI: %ld last applied: %ld " + "share: 0x%lx (%s.%s)", pOp->getEventType(), + (long) gci, + (long) ndb_latest_applied_binlog_epoch, + (long) share, + share ? share->db : "'NULL'", + share ? share->table_name : "'NULL'")); DBUG_ASSERT(share != 0); } // assert that there is consistancy between gci op list @@ -3814,7 +3955,7 @@ restart: else { // set injector_ndb database/schema from table internal name - int ret= + IF_DBUG(int ret=) i_ndb->setDatabaseAndSchemaName(pOp->getEvent()->getTable()); DBUG_ASSERT(ret == 0); ndb_binlog_thread_handle_non_data_event(thd, i_ndb, pOp, row); @@ -3834,9 +3975,10 @@ restart: do_ndbcluster_binlog_close_connection= BCCC_restart; if (ndb_latest_received_binlog_epoch < *p_latest_trans_gci && ndb_binlog_running) { - sql_print_error("NDB Binlog: latest transaction in epoch %lld not in binlog " - "as latest received epoch is %lld", - *p_latest_trans_gci, ndb_latest_received_binlog_epoch); + sql_print_error("NDB Binlog: latest transaction in epoch %lu not in binlog " + "as latest received epoch is %lu", + (ulong) *p_latest_trans_gci, + (ulong) ndb_latest_received_binlog_epoch); } } } @@ -3847,7 +3989,7 @@ restart: /* note! pOp is not referring to an event in the next epoch or is == 0 - */ + */ #ifdef RUN_NDB_BINLOG_TIMER write_timer.stop(); #endif @@ -3868,9 +4010,9 @@ restart: row.master_log_file= start.file_name(); row.master_log_pos= start.file_pos(); - DBUG_PRINT("info", ("COMMIT gci: %lld", gci)); - if (ndb_update_binlog_index) - ndb_add_binlog_index(thd, &row); + DBUG_PRINT("info", ("COMMIT gci: %lu", (ulong) gci)); + if (ndb_update_ndb_binlog_index) + ndb_add_ndb_binlog_index(thd, &row); ndb_latest_applied_binlog_epoch= gci; } ndb_latest_handled_binlog_epoch= gci; @@ -3880,9 +4022,9 @@ restart: "%ld(%d e/s), total time %ld(%d e/s)", (ulong)gci, event_count, write_timer.elapsed_ms(), - event_count / write_timer.elapsed_ms(), + (1000*event_count) / write_timer.elapsed_ms(), gci_timer.elapsed_ms(), - event_count / gci_timer.elapsed_ms()); + (1000*event_count) / gci_timer.elapsed_ms()); #endif } } @@ -3898,19 +4040,16 @@ restart: { ndb_binlog_tables_inited= FALSE; close_thread_tables(thd); - binlog_index= 0; + ndb_binlog_index= 0; goto restart; } err: + sql_print_information("Stopping Cluster Binlog"); DBUG_PRINT("info",("Shutting down cluster binlog thread")); thd->proc_info= "Shutting down"; close_thread_tables(thd); pthread_mutex_lock(&injector_mutex); /* don't mess with the injector_ndb anymore from other threads */ - int ndb_obj_cnt= 1; // g_ndb - ndb_obj_cnt+= injector_ndb == 0 ? 0 : 1; - ndb_obj_cnt+= schema_ndb == 0 ? 0 : 1; - ndb_obj_cnt+= ndbcluster_util_inited ? 1 : 0; injector_thd= 0; injector_ndb= 0; p_latest_trans_gci= 0; @@ -3918,37 +4057,27 @@ err: pthread_mutex_unlock(&injector_mutex); thd->db= 0; // as not to try to free memory - if (!ndb_extra_logging) - sql_print_information("Stopping Cluster Binlog"); - else - sql_print_information("Stopping Cluster Binlog: %u(%u)", - g_ndb_cluster_connection->get_active_ndb_objects(), - ndb_obj_cnt); - - /** - * Add extra wait loop to make user "user" ndb-object go away... - * otherwise user thread can have ongoing SUB_DATA - */ - int sleep_cnt= 0; - while (sleep_cnt < 300 && g_ndb_cluster_connection->get_active_ndb_objects() > ndb_obj_cnt) - { - my_sleep(10000); // 10ms - sleep_cnt++; - } - if (ndb_extra_logging) - sql_print_information("Stopping Cluster Binlog: waited %ums %u(%u)", - 10*sleep_cnt, g_ndb_cluster_connection->get_active_ndb_objects(), - ndb_obj_cnt); - - if (apply_status_share) + if (ndb_apply_status_share) { - free_share(&apply_status_share); - apply_status_share= 0; + /* ndb_share reference binlog extra free */ + DBUG_PRINT("NDB_SHARE", ("%s binlog extra free use_count: %u", + ndb_apply_status_share->key, + ndb_apply_status_share->use_count)); + free_share(&ndb_apply_status_share); + ndb_apply_status_share= 0; } - if (schema_share) + if (ndb_schema_share) { - free_share(&schema_share); - schema_share= 0; + /* begin protect ndb_schema_share */ + pthread_mutex_lock(&ndb_schema_share_mutex); + /* ndb_share reference binlog extra free */ + DBUG_PRINT("NDB_SHARE", ("%s binlog extra free use_count: %u", + ndb_schema_share->key, + ndb_schema_share->use_count)); + free_share(&ndb_schema_share); + ndb_schema_share= 0; + pthread_mutex_unlock(&ndb_schema_share_mutex); + /* end protect ndb_schema_share */ } /* remove all event operations */ @@ -3966,6 +4095,9 @@ err: DBUG_ASSERT(share->op == op || share->op_old == op); share->op= share->op_old= 0; + /* ndb_share reference binlog free */ + DBUG_PRINT("NDB_SHARE", ("%s binlog free use_count: %u", + share->key, share->use_count)); free_share(&share); s_ndb->dropEventOperation(op); } @@ -3986,6 +4118,9 @@ err: DBUG_ASSERT(share->op == op || share->op_old == op); share->op= share->op_old= 0; + /* ndb_share reference binlog free */ + DBUG_PRINT("NDB_SHARE", ("%s binlog free use_count: %u", + share->key, share->use_count)); free_share(&share); i_ndb->dropEventOperation(op); } @@ -3995,8 +4130,8 @@ err: hash_free(&ndb_schema_objects); - // Placed here to avoid a memory leak; TODO: check if needed net_end(&thd->net); + thd->cleanup(); delete thd; ndb_binlog_thread_running= -1; diff --git a/sql/ha_ndbcluster_binlog.h b/sql/ha_ndbcluster_binlog.h index 233d1a58aaa..00fc689f061 100644 --- a/sql/ha_ndbcluster_binlog.h +++ b/sql/ha_ndbcluster_binlog.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -124,6 +123,7 @@ void ndbcluster_binlog_init_handlerton(); void ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *table); bool ndbcluster_check_if_local_table(const char *dbname, const char *tabname); +bool ndbcluster_check_if_local_tables_in_db(THD *thd, const char *dbname); int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key, uint key_len, @@ -156,11 +156,11 @@ int ndbcluster_binlog_start(); pthread_handler_t ndb_binlog_thread_func(void *arg); /* - table cluster_replication.apply_status + table mysql.ndb_apply_status */ int ndbcluster_setup_binlog_table_shares(THD *thd); -extern NDB_SHARE *apply_status_share; -extern NDB_SHARE *schema_share; +extern NDB_SHARE *ndb_apply_status_share; +extern NDB_SHARE *ndb_schema_share; extern THD *injector_thd; extern my_bool ndb_binlog_running; @@ -208,11 +208,6 @@ inline void free_share(NDB_SHARE **share, bool have_lock= FALSE) ndbcluster_free_share(share, have_lock); } -inline void real_free_share(NDB_SHARE **share) -{ - ndbcluster_real_free_share(share); -} - inline Thd_ndb * get_thd_ndb(THD *thd) { return (Thd_ndb *) thd->ha_data[ndbcluster_hton->slot]; } diff --git a/sql/ha_ndbcluster_tables.h b/sql/ha_ndbcluster_tables.h index 12124cd8820..9f7b9146d91 100644 --- a/sql/ha_ndbcluster_tables.h +++ b/sql/ha_ndbcluster_tables.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -15,7 +14,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define NDB_REP_DB "cluster" -#define NDB_REP_TABLE "binlog_index" -#define NDB_APPLY_TABLE "apply_status" -#define NDB_SCHEMA_TABLE "schema" +#define NDB_REP_DB "mysql" +#define NDB_REP_TABLE "ndb_binlog_index" +#define NDB_APPLY_TABLE "ndb_apply_status" +#define OLD_NDB_APPLY_TABLE "apply_status" +#define NDB_SCHEMA_TABLE "ndb_schema" diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 713be1b7b5d..db0d118c2e0 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -159,7 +158,7 @@ static uint alter_table_flags(uint flags __attribute__((unused))) ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share) :handler(hton, share), m_part_info(NULL), m_create_handler(FALSE), - m_is_sub_partitioned(0) + m_is_sub_partitioned(0), is_clone(FALSE) { DBUG_ENTER("ha_partition::ha_partition(table)"); init_handler_variables(); @@ -181,8 +180,7 @@ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share) ha_partition::ha_partition(handlerton *hton, partition_info *part_info) :handler(hton, NULL), m_part_info(part_info), m_create_handler(TRUE), - m_is_sub_partitioned(m_part_info->is_sub_partitioned()) - + m_is_sub_partitioned(m_part_info->is_sub_partitioned()), is_clone(FALSE) { DBUG_ENTER("ha_partition::ha_partition(part_info)"); init_handler_variables(); @@ -585,7 +583,6 @@ int ha_partition::drop_partitions(const char *path) List_iterator<partition_element> part_it(m_part_info->partitions); char part_name_buff[FN_REFLEN]; uint no_parts= m_part_info->partitions.elements; - uint part_count= 0; uint no_subparts= m_part_info->no_subparts; uint i= 0; uint name_variant; @@ -1076,7 +1073,6 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, uint no_parts= m_part_info->no_parts; uint no_subparts= m_part_info->no_subparts; uint i= 0; - LEX *lex= thd->lex; int error; DBUG_ENTER("ha_partition::handle_opt_partitions"); DBUG_PRINT("enter", ("all_parts %u, flag= %u", all_parts, flag)); @@ -1088,11 +1084,9 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, { if (m_is_sub_partitioned) { - List_iterator<partition_element> sub_it(part_elem->subpartitions); uint j= 0, part; do { - partition_element *sub_elem= sub_it++; part= i * no_subparts + j; DBUG_PRINT("info", ("Optimize subpartition %u", part)); @@ -1137,7 +1131,6 @@ int ha_partition::prepare_new_partition(TABLE *table, { int error; bool create_flag= FALSE; - bool open_flag= FALSE; DBUG_ENTER("prepare_new_partition"); if ((error= set_up_table_before_create(table, part_name, create_info, @@ -1246,7 +1239,6 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, handler **new_file_array; int error= 1; bool first; - bool copy_parts= FALSE; uint temp_partitions= m_part_info->temp_partitions.elements; THD *thd= current_thd; DBUG_ENTER("ha_partition::change_partitions"); @@ -2027,7 +2019,7 @@ bool ha_partition::create_handlers(MEM_ROOT *mem_root) if (!(m_file[i]= get_new_handler(table_share, mem_root, m_engine_array[i]))) DBUG_RETURN(TRUE); - DBUG_PRINT("info", ("engine_type: %u", m_engine_array[i])); + DBUG_PRINT("info", ("engine_type: %u", m_engine_array[i]->db_type)); } /* For the moment we only support partition over the same table engine */ if (m_engine_array[0] == myisam_hton) @@ -2062,7 +2054,6 @@ bool ha_partition::new_handlers_from_part_info(MEM_ROOT *mem_root) partition_element *part_elem; uint alloc_len= (m_tot_parts + 1) * sizeof(handler*); List_iterator_fast <partition_element> part_it(m_part_info->partitions); - THD *thd= current_thd; DBUG_ENTER("ha_partition::new_handlers_from_part_info"); if (!(m_file= (handler **) alloc_root(mem_root, alloc_len))) @@ -2270,9 +2261,12 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) } /* Initialise the bitmap we use to determine what partitions are used */ - if (bitmap_init(&(m_part_info->used_partitions), NULL, m_tot_parts, TRUE)) - DBUG_RETURN(1); - bitmap_set_all(&(m_part_info->used_partitions)); + if (!is_clone) + { + if (bitmap_init(&(m_part_info->used_partitions), NULL, m_tot_parts, TRUE)) + DBUG_RETURN(1); + bitmap_set_all(&(m_part_info->used_partitions)); + } /* Recalculate table flags as they may change after open */ m_table_flags= m_file[0]->table_flags(); @@ -2328,6 +2322,19 @@ err_handler: DBUG_RETURN(error); } +handler *ha_partition::clone(MEM_ROOT *mem_root) +{ + handler *new_handler= get_new_handler(table->s, mem_root, table->s->db_type); + ((ha_partition*)new_handler)->m_part_info= m_part_info; + ((ha_partition*)new_handler)->is_clone= TRUE; + if (new_handler && !new_handler->ha_open(table, + table->s->normalized_path.str, + table->db_stat, + HA_OPEN_IGNORE_IF_LOCKED)) + return new_handler; + return NULL; +} + /* Close handler object @@ -2354,7 +2361,8 @@ int ha_partition::close(void) DBUG_ENTER("ha_partition::close"); delete_queue(&m_queue); - bitmap_free(&(m_part_info->used_partitions)); + if (!is_clone) + bitmap_free(&(m_part_info->used_partitions)); file= m_file; repeat: @@ -2427,7 +2435,7 @@ repeat: do { DBUG_PRINT("info", ("external_lock(thd, %d) iteration %d", - lock_type, (file - m_file))); + lock_type, (int) (file - m_file))); if ((error= (*file)->external_lock(thd, lock_type))) { if (F_UNLCK != lock_type) @@ -2508,7 +2516,7 @@ THR_LOCK_DATA **ha_partition::store_lock(THD *thd, file= m_file; do { - DBUG_PRINT("info", ("store lock %d iteration", (file - m_file))); + DBUG_PRINT("info", ("store lock %d iteration", (int) (file - m_file))); to= (*file)->store_lock(thd, to, lock_type); } while (*(++file)); DBUG_RETURN(to); @@ -2939,8 +2947,8 @@ int ha_partition::rnd_init(bool scan) include_partition_fields_in_used_fields(); /* Now we see what the index of our first important partition is */ - DBUG_PRINT("info", ("m_part_info->used_partitions 0x%x", - m_part_info->used_partitions.bitmap)); + DBUG_PRINT("info", ("m_part_info->used_partitions: 0x%lx", + (long) m_part_info->used_partitions.bitmap)); part_id= bitmap_get_first_set(&(m_part_info->used_partitions)); DBUG_PRINT("info", ("m_part_spec.start_part %d", part_id)); @@ -4016,6 +4024,7 @@ int ha_partition::handle_ordered_index_scan(byte *buf, bool reverse_order) m_queue.elements= j; queue_fix(&m_queue); return_top_record(buf); + table->status= 0; DBUG_PRINT("info", ("Record returned from partition %d", m_top_entry)); DBUG_RETURN(0); } @@ -4084,6 +4093,7 @@ int ha_partition::handle_ordered_next(byte *buf, bool is_next_same) DBUG_PRINT("info", ("Record returned from partition %u (2)", m_top_entry)); return_top_record(buf); + table->status= 0; error= 0; } } @@ -4127,6 +4137,7 @@ int ha_partition::handle_ordered_prev(byte *buf) DBUG_PRINT("info", ("Record returned from partition %d (2)", m_top_entry)); error= 0; + table->status= 0; } } DBUG_RETURN(error); @@ -4241,7 +4252,7 @@ void ha_partition::include_partition_fields_in_used_fields() retrieving statistics data. */ -void ha_partition::info(uint flag) +int ha_partition::info(uint flag) { handler *file, **file_array; DBUG_ENTER("ha_partition:info"); @@ -4405,7 +4416,7 @@ void ha_partition::info(uint flag) stats.update_time= file->stats.update_time; } while (*(++file_array)); } - DBUG_VOID_RETURN; + DBUG_RETURN(0); } @@ -4471,7 +4482,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info, 2) It is called from close_thread_table which in turn is called from close_thread_tables except in the case where the tables are locked in which case ha_commit_stmt is called instead. - It is only called from here if flush_version hasn't changed and the + It is only called from here if refresh_version hasn't changed and the table is not an old table when calling close_thread_table. close_thread_tables is called from many places as a general clean up function after completing a query. @@ -4492,8 +4503,9 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info, The handler will set HA_KEYREAD_ONLY in its table flags to indicate this feature is supported. HA_EXTRA_FLUSH: - Indication to flush tables to disk, called at close_thread_table to + Indication to flush tables to disk, is supposed to be used to ensure disk based tables are flushed at end of query execution. + Currently is never used. 2) Parameters used by some non-MyISAM handlers ---------------------------------------------- diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 008bda01bc9..1069dd058d5 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -76,12 +75,12 @@ private: for this since the MySQL Server sometimes allocating the handler object without freeing them. */ - u_long m_table_flags; - u_long m_low_byte_first; + longlong m_table_flags; + ulong m_low_byte_first; uint m_reorged_parts; // Number of reorganised parts uint m_tot_parts; // Total number of partitions; - uint m_no_locks; // For engines like ha_blackhole, which needs no locks + uint m_no_locks; // For engines like ha_blackhole, which needs no locks uint m_last_part; // Last file that we update,write int m_lock_type; // Remembers type of last // external_lock @@ -133,7 +132,13 @@ private: THR_LOCK_DATA lock; /* MySQL lock */ PARTITION_SHARE *share; /* Shared lock info */ + /* + TRUE <=> this object was created with ha_partition::clone and doesn't + "own" the m_part_info structure. + */ + bool is_clone; public: + handler *clone(MEM_ROOT *mem_root); virtual void set_part_info(partition_info *part_info) { m_part_info= part_info; @@ -450,7 +455,7 @@ public: purposes. ------------------------------------------------------------------------- */ - virtual void info(uint); + virtual int info(uint); void get_dynamic_partition_info(PARTITION_INFO *stat_info, uint part_id); virtual int extra(enum ha_extra_function operation); diff --git a/sql/handler.cc b/sql/handler.cc index fc8bc939961..23c3103493e 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -14,8 +13,11 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/** @file handler.cc -/* Handler-calling-functions */ + @brief + Handler-calling-functions +*/ #ifdef USE_PRAGMA_IMPLEMENTATION #pragma implementation // gcc: Class implementation @@ -45,12 +47,6 @@ static handlerton *installed_htons[128]; KEY_CREATE_INFO default_key_create_info= { HA_KEY_ALG_UNDEF, 0, {NullS,0} }; -/* static functions defined in this file */ - -static handler *create_default(TABLE_SHARE *table, MEM_ROOT *mem_root); - -static SHOW_COMP_OPTION have_yes= SHOW_OPTION_YES; - /* number of entries in handlertons[] */ ulong total_ha= 0; /* number of storage engines (from handlertons[]) that support 2pc */ @@ -81,17 +77,16 @@ static TYPELIB known_extensions= {0,"known_exts", NULL, NULL}; uint known_extensions_id= 0; -/* +/** @brief Return the default storage engine handlerton for thread - + SYNOPSIS ha_default_handlerton(thd) thd current thread - + RETURN pointer to handlerton */ - handlerton *ha_default_handlerton(THD *thd) { return (thd->variables.table_type != NULL) ? @@ -101,7 +96,7 @@ handlerton *ha_default_handlerton(THD *thd) } -/* +/** @brief Return the storage engine handlerton for the supplied name SYNOPSIS @@ -112,7 +107,6 @@ handlerton *ha_default_handlerton(THD *thd) RETURN pointer to handlerton */ - handlerton *ha_resolve_by_name(THD *thd, const LEX_STRING *name) { const LEX_STRING *table_alias; @@ -167,11 +161,13 @@ const char *ha_get_storage_engine(enum legacy_db_type db_type) } +#ifdef NOT_USED static handler *create_default(TABLE_SHARE *table, MEM_ROOT *mem_root) { handlerton *hton= ha_default_handlerton(current_thd); return (hton && hton->create) ? hton->create(hton, table, mem_root) : NULL; } +#endif handlerton *ha_resolve_by_legacy_type(THD *thd, enum legacy_db_type db_type) @@ -189,8 +185,9 @@ handlerton *ha_resolve_by_legacy_type(THD *thd, enum legacy_db_type db_type) } -/* Use other database handler if databasehandler is not compiled in */ - +/** @brief + Use other database handler if databasehandler is not compiled in +*/ handlerton *ha_checktype(THD *thd, enum legacy_db_type database_type, bool no_substitute, bool report_error) { @@ -270,7 +267,7 @@ handler *get_ha_partition(partition_info *part_info) #endif -/* +/** @brief Register handler error messages for use with my_error(). SYNOPSIS @@ -280,7 +277,6 @@ handler *get_ha_partition(partition_info *part_info) 0 OK != 0 Error */ - static int ha_init_errors(void) { #define SETMSG(nr, msg) errmsgs[(nr) - HA_ERR_FIRST]= (msg) @@ -340,7 +336,7 @@ static int ha_init_errors(void) } -/* +/** @brief Unregister handler error messages. SYNOPSIS @@ -350,7 +346,6 @@ static int ha_init_errors(void) 0 OK != 0 Error */ - static int ha_finish_errors(void) { const char **errmsgs; @@ -376,11 +371,12 @@ int ha_finalize_handlerton(st_plugin_int *plugin) case SHOW_OPTION_YES: if (installed_htons[hton->db_type] == hton) installed_htons[hton->db_type]= NULL; - if (hton->panic && hton->panic(hton, HA_PANIC_CLOSE)) - DBUG_RETURN(1); break; }; + if (hton->panic) + hton->panic(hton, HA_PANIC_CLOSE); + if (plugin->plugin->deinit) { /* @@ -509,31 +505,22 @@ int ha_init() DBUG_RETURN(error); } -/* - close, flush or restart databases - Ignore this for other databases than ours -*/ - -static my_bool panic_handlerton(THD *unused1, st_plugin_int *plugin, void *arg) +int ha_end() { - handlerton *hton= (handlerton *)plugin->data; - if (hton->state == SHOW_OPTION_YES && hton->panic) - ((int*)arg)[0]|= hton->panic(hton, (enum ha_panic_function)((int*)arg)[1]); - return FALSE; -} - + int error= 0; + DBUG_ENTER("ha_end"); -int ha_panic(enum ha_panic_function flag) -{ - int error[2]; - error[0]= 0; error[1]= (int)flag; - plugin_foreach(NULL, panic_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, error); + /* + This should be eventualy based on the graceful shutdown flag. + So if flag is equal to HA_PANIC_CLOSE, the deallocate + the errors. + */ + if (ha_finish_errors()) + error= 1; - if (flag == HA_PANIC_CLOSE && ha_finish_errors()) - error[0]= 1; - return error[0]; -} /* ha_panic */ + DBUG_RETURN(error); +} static my_bool dropdb_handlerton(THD *unused1, st_plugin_int *plugin, void *path) @@ -566,7 +553,9 @@ static my_bool closecon_handlerton(THD *thd, st_plugin_int *plugin, } -/* don't bother to rollback here, it's done already */ +/** @brief + don't bother to rollback here, it's done already +*/ void ha_close_connection(THD* thd) { plugin_foreach(thd, closecon_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, 0); @@ -575,7 +564,7 @@ void ha_close_connection(THD* thd) /* ======================================================================== ======================= TRANSACTIONS ===================================*/ -/* +/** @brief Register a storage engine for a transaction DESCRIPTION @@ -617,7 +606,7 @@ void trans_register_ha(THD *thd, bool all, handlerton *ht_arg) DBUG_VOID_RETURN; } -/* +/** @brief RETURN 0 - ok 1 - error, transaction was rolled back @@ -657,7 +646,7 @@ int ha_prepare(THD *thd) DBUG_RETURN(error); } -/* +/** @brief RETURN 0 - ok 1 - transaction was rolled back @@ -700,6 +689,19 @@ int ha_commit_trans(THD *thd, bool all) ha_rollback_trans(thd, all); DBUG_RETURN(1); } + + if ( is_real_trans + && opt_readonly + && ! (thd->security_ctx->master_access & SUPER_ACL) + && ! thd->slave_thread + ) + { + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); + ha_rollback_trans(thd, all); + error= 1; + goto end; + } + DBUG_EXECUTE_IF("crash_commit_before", abort();); /* Close all cursors that can not survive COMMIT */ @@ -720,7 +722,7 @@ int ha_commit_trans(THD *thd, bool all) } DBUG_EXECUTE_IF("crash_commit_after_prepare", abort();); if (error || (is_real_trans && xid && - (error= !(cookie= tc_log->log(thd, xid))))) + (error= !(cookie= tc_log->log_xid(thd, xid))))) { ha_rollback_trans(thd, all); error= 1; @@ -728,7 +730,7 @@ int ha_commit_trans(THD *thd, bool all) } DBUG_EXECUTE_IF("crash_commit_after_log", abort();); } - error=ha_commit_one_phase(thd, all) ? cookie ? 2 : 1 : 0; + error=ha_commit_one_phase(thd, all) ? (cookie ? 2 : 1) : 0; DBUG_EXECUTE_IF("crash_commit_before_unlog", abort();); if (cookie) tc_log->unlog(cookie, xid); @@ -741,7 +743,7 @@ end: DBUG_RETURN(error); } -/* +/** @brief NOTE - this function does not care about global read lock. A caller should. */ @@ -843,14 +845,14 @@ int ha_rollback_trans(THD *thd, bool all) message in the error log, so we don't send it. */ if (is_real_trans && (thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && - !thd->slave_thread) + !thd->slave_thread && thd->killed != THD::KILL_CONNECTION) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARNING_NOT_COMPLETE_ROLLBACK, ER(ER_WARNING_NOT_COMPLETE_ROLLBACK)); DBUG_RETURN(error); } -/* +/** @brief This is used to commit or rollback a single statement depending on the value of error. Note that if the autocommit is on, then the following call inside InnoDB will commit or rollback the whole transaction (= the statement). The @@ -858,7 +860,6 @@ int ha_rollback_trans(THD *thd, bool all) the user has used LOCK TABLES then that mechanism does not know to do the commit. */ - int ha_autocommit_or_rollback(THD *thd, int error) { DBUG_ENTER("ha_autocommit_or_rollback"); @@ -976,7 +977,7 @@ static char* xid_to_str(char *buf, XID *xid) } #endif -/* +/** @brief recover() step of xa NOTE @@ -995,7 +996,6 @@ static char* xid_to_str(char *buf, XID *xid) in this case commit_list==0, tc_heuristic_recover == 0 there should be no prepared transactions in this case. */ - struct xarecover_st { int len, found_foreign_xids, found_my_xids; @@ -1127,7 +1127,7 @@ int ha_recover(HASH *commit_list) DBUG_RETURN(0); } -/* +/** @brief return the list of XID's to a client, the same way SHOW commands do NOTE @@ -1176,7 +1176,7 @@ bool mysql_xa_recover(THD *thd) DBUG_RETURN(0); } -/* +/** @brief This function should be called when MySQL sends rows of a SELECT result set or the EOF mark to the client. It releases a possible adaptive hash index S-latch held by thd in InnoDB and also releases a possible InnoDB query @@ -1192,7 +1192,6 @@ bool mysql_xa_recover(THD *thd) thd: the thread handle of the current connection return value: always 0 */ - static my_bool release_temporary_latches(THD *thd, st_plugin_int *plugin, void *unused) { @@ -1259,12 +1258,11 @@ int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv) DBUG_RETURN(error); } -/* +/** @brief note, that according to the sql standard (ISO/IEC 9075-2:2003) section "4.33.4 SQL-statements and transaction states", SAVEPOINT is *not* transaction-initiating SQL-statement */ - int ha_savepoint(THD *thd, SAVEPOINT *sv) { int error=0; @@ -1379,11 +1377,10 @@ bool ha_flush_logs(handlerton *db_type) return FALSE; } -/* +/** @brief This should return ENOENT if the file doesn't exists. The .frm file will be deleted only if we return 0 or ENOENT */ - int ha_delete_table(THD *thd, handlerton *table_type, const char *path, const char *db, const char *alias, bool generate_warning) { @@ -1483,8 +1480,9 @@ bool handler::check_if_log_table_locking_is_allowed(uint sql_command, { /* Deny locking of the log tables, which is incompatible with - concurrent insert. Unless called from a logger THD: - general_log_thd or slow_log_thd. + concurrent insert. The routine is not called if the table is + being locked from a logger THD (general_log_thd or slow_log_thd) + or from a privileged thread (see log.cc for details) */ if (table->s->log_table && sql_command != SQLCOM_TRUNCATE && @@ -1505,14 +1503,13 @@ bool handler::check_if_log_table_locking_is_allowed(uint sql_command, return TRUE; } -/* +/** @brief Open database-handler. IMPLEMENTATION Try O_RDONLY if cannot open as O_RDWR Don't wait for locks if not HA_OPEN_WAIT_IF_LOCKED is set */ - int handler::ha_open(TABLE *table_arg, const char *name, int mode, int test_if_locked) { @@ -1520,7 +1517,7 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode, DBUG_ENTER("handler::ha_open"); DBUG_PRINT("enter", ("name: %s db_type: %d db_stat: %d mode: %d lock_test: %d", - name, table_share->db_type, table_arg->db_stat, mode, + name, ht->db_type, table_arg->db_stat, mode, test_if_locked)); table= table_arg; @@ -1560,12 +1557,11 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode, } -/* +/** @brief Read first row (only) from a table This is never called for InnoDB tables, as these table types has the HA_STATS_RECORDS_IS_EXACT set. */ - int handler::read_first_row(byte * buf, uint primary_key) { register int error; @@ -1596,7 +1592,7 @@ int handler::read_first_row(byte * buf, uint primary_key) DBUG_RETURN(error); } -/* +/** @brief Generate the next auto-increment number based on increment and offset: computes the lowest number - strictly greater than "nr" @@ -1607,7 +1603,6 @@ int handler::read_first_row(byte * buf, uint primary_key) If increment=10 and offset=5 and previous number is 1, we get: 1,5,15,25,35,... */ - inline ulonglong compute_next_insert_id(ulonglong nr,struct system_variables *variables) { @@ -1633,7 +1628,7 @@ void handler::adjust_next_insert_id_after_explicit_value(ulonglong nr) } -/* +/** @brief Computes the largest number X: - smaller than or equal to "nr" - of the form: auto_increment_offset + N * auto_increment_increment @@ -1648,7 +1643,6 @@ void handler::adjust_next_insert_id_after_explicit_value(ulonglong nr) RETURN The number X if it exists, "nr" otherwise. */ - inline ulonglong prev_insert_id(ulonglong nr, struct system_variables *variables) { @@ -1661,7 +1655,7 @@ prev_insert_id(ulonglong nr, struct system_variables *variables) */ DBUG_PRINT("info",("auto_increment: nr: %lu cannot honour " "auto_increment_offset: %lu", - nr, variables->auto_increment_offset)); + (ulong) nr, variables->auto_increment_offset)); return nr; } if (variables->auto_increment_increment == 1) @@ -1917,7 +1911,7 @@ int handler::update_auto_increment() } -/* +/** @brief MySQL signal that it changed the column bitmap USAGE @@ -1930,17 +1924,16 @@ int handler::update_auto_increment() rnd_init() call is made as after this, MySQL will not use the bitmap for any program logic checking. */ - void handler::column_bitmaps_signal() { DBUG_ENTER("column_bitmaps_signal"); - DBUG_PRINT("info", ("read_set: 0x%lx write_set: 0x%lx", table->read_set, - table->write_set)); + DBUG_PRINT("info", ("read_set: 0x%lx write_set: 0x%lx", (long) table->read_set, + (long) table->write_set)); DBUG_VOID_RETURN; } -/* +/** @brief Reserves an interval of auto_increment values from the handler. SYNOPSIS @@ -1957,7 +1950,6 @@ void handler::column_bitmaps_signal() If the function sets *nb_reserved_values to ULONGLONG_MAX it means it has reserved to "positive infinite". */ - void handler::get_auto_increment(ulonglong offset, ulonglong increment, ulonglong nb_desired_values, ulonglong *first_value, @@ -2031,20 +2023,31 @@ void handler::print_keydup_error(uint key_nr, const char *msg) /* Write the duplicated key in the error message */ char key[MAX_KEY_LENGTH]; String str(key,sizeof(key),system_charset_info); - /* Table is opened and defined at this point */ - key_unpack(&str,table,(uint) key_nr); - uint max_length=MYSQL_ERRMSG_SIZE-(uint) strlen(msg); - if (str.length() >= max_length) + + if (key_nr == MAX_KEY) { - str.length(max_length-4); - str.append(STRING_WITH_LEN("...")); + /* Key is unknown */ + str.copy("", 0, system_charset_info); + my_printf_error(ER_DUP_ENTRY_WITH_KEY_NAME, msg, + MYF(0), str.c_ptr(), "*UNKNOWN*"); + } + else + { + /* Table is opened and defined at this point */ + key_unpack(&str,table,(uint) key_nr); + uint max_length=MYSQL_ERRMSG_SIZE-(uint) strlen(msg); + if (str.length() >= max_length) + { + str.length(max_length-4); + str.append(STRING_WITH_LEN("...")); + } + my_printf_error(ER_DUP_ENTRY_WITH_KEY_NAME, msg, + MYF(0), str.c_ptr(), table->key_info[key_nr].name); } - my_printf_error(ER_DUP_ENTRY, msg, - MYF(0), str.c_ptr(), table->key_info[key_nr].name); } -/* +/** @brief Print error that we got from handler function NOTE @@ -2053,7 +2056,6 @@ void handler::print_keydup_error(uint key_nr, const char *msg) table->s->path table->alias */ - void handler::print_error(int error, myf errflag) { DBUG_ENTER("handler::print_error"); @@ -2083,7 +2085,7 @@ void handler::print_error(int error, myf errflag) uint key_nr=get_dup_key(error); if ((int) key_nr >= 0) { - print_keydup_error(key_nr, ER(ER_DUP_ENTRY)); + print_keydup_error(key_nr, ER(ER_DUP_ENTRY_WITH_KEY_NAME)); DBUG_VOID_RETURN; } textno=ER_DUP_KEY; @@ -2115,8 +2117,8 @@ void handler::print_error(int error, myf errflag) break; } case HA_ERR_NULL_IN_SPATIAL: - textno= ER_UNKNOWN_ERROR; - break; + my_error(ER_CANT_CREATE_GEOMETRY_OBJECT, MYF(0)); + DBUG_VOID_RETURN; case HA_ERR_FOUND_DUPP_UNIQUE: textno=ER_DUP_UNIQUE; break; @@ -2239,7 +2241,7 @@ void handler::print_error(int error, myf errflag) } -/* +/** @brief Return an error message specific to this handler SYNOPSIS @@ -2247,8 +2249,7 @@ void handler::print_error(int error, myf errflag) buf Pointer to String where to add error message Returns true if this is a temporary error - */ - +*/ bool handler::get_error_message(int error, String* buf) { return FALSE; @@ -2274,7 +2275,7 @@ int handler::ha_check_for_upgrade(HA_CHECK_OPT *check_opt) if (!keypart->fieldnr) continue; Field *field= table->field[keypart->fieldnr-1]; - if (field->type() == FIELD_TYPE_BLOB) + if (field->type() == MYSQL_TYPE_BLOB) { if (check_opt->sql_flags & TT_FOR_UPGRADE) check_opt->flags= T_MEDIUM; @@ -2296,7 +2297,11 @@ int handler::check_old_types() /* check for bad DECIMAL field */ for (field= table->field; (*field); field++) { - if ((*field)->type() == FIELD_TYPE_NEWDECIMAL) + if ((*field)->type() == MYSQL_TYPE_NEWDECIMAL) + { + return HA_ADMIN_NEEDS_ALTER; + } + if ((*field)->type() == MYSQL_TYPE_VAR_STRING) { return HA_ADMIN_NEEDS_ALTER; } @@ -2306,7 +2311,7 @@ int handler::check_old_types() } -static bool update_frm_version(TABLE *table, bool needs_lock) +static bool update_frm_version(TABLE *table) { char path[FN_REFLEN]; File file; @@ -2318,9 +2323,6 @@ static bool update_frm_version(TABLE *table, bool needs_lock) strxmov(path, table->s->normalized_path.str, reg_ext, NullS); - if (needs_lock) - pthread_mutex_lock(&LOCK_open); - if ((file= my_open(path, O_RDWR|O_BINARY, MYF(MY_WME))) >= 0) { uchar version[4]; @@ -2342,15 +2344,13 @@ static bool update_frm_version(TABLE *table, bool needs_lock) err: if (file >= 0) VOID(my_close(file,MYF(MY_WME))); - if (needs_lock) - pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(result); } -/* Return key if error because of duplicated keys */ - +/** @brief + Return key if error because of duplicated keys */ uint handler::get_dup_key(int error) { DBUG_ENTER("handler::get_dup_key"); @@ -2363,7 +2363,7 @@ uint handler::get_dup_key(int error) } -/* +/** @brief Delete all files with extension from bas_ext() SYNOPSIS @@ -2379,7 +2379,6 @@ uint handler::get_dup_key(int error) didn't get any other errors than ENOENT # Error */ - int handler::delete_table(const char *name) { int error= 0; @@ -2425,7 +2424,7 @@ void handler::drop_table(const char *name) } -/* +/** @brief Performs checks upon the table. SYNOPSIS @@ -2441,7 +2440,6 @@ void handler::drop_table(const char *name) HA_ADMIN_NEEDS_ALTER Table has structures requiring ALTER TABLE HA_ADMIN_NOT_IMPLEMENTED */ - int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt) { int error; @@ -2462,7 +2460,7 @@ int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt) } if ((error= check(thd, check_opt))) return error; - return update_frm_version(table, 0); + return update_frm_version(table); } @@ -2471,11 +2469,11 @@ int handler::ha_repair(THD* thd, HA_CHECK_OPT* check_opt) int result; if ((result= repair(thd, check_opt))) return result; - return update_frm_version(table, 0); + return update_frm_version(table); } -/* +/** @brief Tell the storage engine that it is allowed to "disable transaction" in the handler. It is a hint that ACID is not required - it is used in NDB for ALTER TABLE, for example, when data are copied to temporary table. @@ -2483,7 +2481,6 @@ int handler::ha_repair(THD* thd, HA_CHECK_OPT* check_opt) starts to commit every now and then automatically. This hint can be safely ignored. */ - int ha_enable_transaction(THD *thd, bool on) { int error=0; @@ -2544,7 +2541,7 @@ void handler::get_dynamic_partition_info(PARTITION_INFO *stat_info, ** Some general functions that isn't in the handler class ****************************************************************************/ -/* +/** @brief Initiates table-file and calls appropriate database-creator NOTES @@ -2555,7 +2552,6 @@ void handler::get_dynamic_partition_info(PARTITION_INFO *stat_info, 0 ok 1 error */ - int ha_create_table(THD *thd, const char *path, const char *db, const char *table_name, HA_CREATE_INFO *create_info, @@ -2599,7 +2595,7 @@ err: DBUG_RETURN(error != 0); } -/* +/** @brief Try to discover table from engine NOTES @@ -2611,7 +2607,6 @@ err: > 0 Error, table existed but could not be created */ - int ha_create_table_from_engine(THD* thd, const char *db, const char *name) { int error; @@ -2686,9 +2681,9 @@ void st_ha_check_opt::init() call to ha_init_key_cache() (probably out of memory) *****************************************************************************/ -/* Init a key cache if it has not been initied before */ - - +/** @brief + Init a key cache if it has not been initied before +*/ int ha_init_key_cache(const char *name, KEY_CACHE *key_cache) { DBUG_ENTER("ha_init_key_cache"); @@ -2710,8 +2705,9 @@ int ha_init_key_cache(const char *name, KEY_CACHE *key_cache) } -/* Resize key cache */ - +/** @brief + Resize key cache +*/ int ha_resize_key_cache(KEY_CACHE *key_cache) { DBUG_ENTER("ha_resize_key_cache"); @@ -2732,8 +2728,9 @@ int ha_resize_key_cache(KEY_CACHE *key_cache) } -/* Change parameters for key cache (like size) */ - +/** @brief + Change parameters for key cache (like size) +*/ int ha_change_key_cache_param(KEY_CACHE *key_cache) { if (key_cache->key_cache_inited) @@ -2747,16 +2744,18 @@ int ha_change_key_cache_param(KEY_CACHE *key_cache) return 0; } -/* Free memory allocated by a key cache */ - +/** @brief + Free memory allocated by a key cache +*/ int ha_end_key_cache(KEY_CACHE *key_cache) { end_key_cache(key_cache, 1); // Can never fail return 0; } -/* Move all tables from one key cache to another one */ - +/** @brief + Move all tables from one key cache to another one +*/ int ha_change_key_cache(KEY_CACHE *old_key_cache, KEY_CACHE *new_key_cache) { @@ -2765,7 +2764,7 @@ int ha_change_key_cache(KEY_CACHE *old_key_cache, } -/* +/** @brief Try to discover one table from handler(s) RETURN @@ -2773,8 +2772,7 @@ int ha_change_key_cache(KEY_CACHE *old_key_cache, 0 : OK. In this case *frmblob and *frmlen are set >0 : error. frmblob and frmlen may not be set */ - -typedef struct st_discover_args +struct st_discover_args { const char *db; const char *name; @@ -2817,12 +2815,12 @@ int ha_discover(THD *thd, const char *db, const char *name, } -/* +/** @brief Call this function in order to give the handler the possibility to ask engine if there are any new tables that should be written to disk or any dropped tables that need to be removed from disk */ -typedef struct st_find_files_args +struct st_find_files_args { const char *db; const char *path; @@ -2852,8 +2850,8 @@ ha_find_files(THD *thd,const char *db,const char *path, { int error= 0; DBUG_ENTER("ha_find_files"); - DBUG_PRINT("enter", ("db: %s, path: %s, wild: %s, dir: %d", - db, path, wild, dir)); + DBUG_PRINT("enter", ("db: '%s' path: '%s' wild: '%s' dir: %d", + db, path, wild ? wild : "NULL", dir)); st_find_files_args args= {db, path, wild, dir, files}; plugin_foreach(thd, find_files_handlerton, @@ -2863,7 +2861,7 @@ ha_find_files(THD *thd,const char *db,const char *path, } -/* +/** @brief Ask handler if the table exists in engine RETURN @@ -2871,9 +2869,8 @@ ha_find_files(THD *thd,const char *db,const char *path, 1 Table exists # Error code - */ - -typedef struct st_table_exists_in_engine_args +*/ +struct st_table_exists_in_engine_args { const char *db; const char *name; @@ -2924,7 +2921,7 @@ struct binlog_func_st void *arg; }; -/* +/** @brief Listing handlertons first to avoid recursive calls and deadlock */ static my_bool binlog_func_list(THD *thd, st_plugin_int *plugin, void *arg) @@ -2947,15 +2944,15 @@ static my_bool binlog_func_list(THD *thd, st_plugin_int *plugin, void *arg) static my_bool binlog_func_foreach(THD *thd, binlog_func_st *bfn) { - handlerton *hton; hton_list_st hton_list; + uint i, sz; + hton_list.sz= 0; plugin_foreach(thd, binlog_func_list, MYSQL_STORAGE_ENGINE_PLUGIN, &hton_list); - uint i= 0, sz= hton_list.sz; - while(i < sz) - hton_list.hton[i++]->binlog_func(hton, thd, bfn->fn, bfn->arg); + for (i= 0, sz= hton_list.sz; i < sz ; i++) + hton_list.hton[i]->binlog_func(hton_list.hton[i], thd, bfn->fn, bfn->arg); return FALSE; } @@ -3042,7 +3039,7 @@ void ha_binlog_log_query(THD *thd, handlerton *hton, } #endif -/* +/** @brief Read the first row of a multi-range set. SYNOPSIS @@ -3066,7 +3063,6 @@ void ha_binlog_log_query(THD *thd, handlerton *hton, HA_ERR_END_OF_FILE No rows in range # Error code */ - int handler::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, KEY_MULTI_RANGE *ranges, uint range_count, bool sorted, HANDLER_BUFFER *buffer) @@ -3099,7 +3095,7 @@ int handler::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, } -/* +/** @brief Read the next row of a multi-range set. SYNOPSIS @@ -3117,7 +3113,6 @@ int handler::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, HA_ERR_END_OF_FILE No (more) rows in range # Error code */ - int handler::read_multi_range_next(KEY_MULTI_RANGE **found_range_p) { int result; @@ -3170,7 +3165,7 @@ int handler::read_multi_range_next(KEY_MULTI_RANGE **found_range_p) } -/* +/** @brief Read first row between two ranges. Store ranges for future calls to read_range_next @@ -3189,7 +3184,6 @@ int handler::read_multi_range_next(KEY_MULTI_RANGE **found_range_p) HA_ERR_END_OF_FILE No rows in range # Error code */ - int handler::read_range_first(const key_range *start_key, const key_range *end_key, bool eq_range_arg, bool sorted) @@ -3224,7 +3218,7 @@ int handler::read_range_first(const key_range *start_key, } -/* +/** @brief Read next row between two ranges. SYNOPSIS @@ -3238,7 +3232,6 @@ int handler::read_range_first(const key_range *start_key, HA_ERR_END_OF_FILE No rows in range # Error code */ - int handler::read_range_next() { int result; @@ -3258,7 +3251,7 @@ int handler::read_range_next() } -/* +/** @brief Compare if found key (in row) is over max-value SYNOPSIS @@ -3275,7 +3268,6 @@ int handler::read_range_next() -1 Key is less than range 1 Key is larger than range */ - int handler::compare_key(key_range *range) { int cmp; @@ -3299,7 +3291,7 @@ int handler::index_read_idx(byte * buf, uint index, const byte * key, } -/* +/** @brief Returns a list of all known extensions. SYNOPSIS @@ -3313,7 +3305,6 @@ int handler::index_read_idx(byte * buf, uint index, const byte * key, RETURN VALUE pointer pointer to TYPELIB structure */ - static my_bool exts_handlerton(THD *unused, st_plugin_int *plugin, void *arg) { @@ -3345,7 +3336,6 @@ static my_bool exts_handlerton(THD *unused, st_plugin_int *plugin, TYPELIB *ha_known_exts(void) { - MEM_ROOT *mem_root= current_thd->mem_root; if (!known_extensions.type_names || mysys_usage_id != known_extensions_id) { List<char> found_exts; @@ -3451,7 +3441,6 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat) - table is not mysql.event */ -#ifdef HAVE_ROW_BASED_REPLICATION /* The Sun compiler cannot instantiate the template below if this is declared static, but it works by putting it into an anonymous namespace. */ @@ -3462,7 +3451,7 @@ namespace { { int const check(table->s->tmp_table == NO_TMP_TABLE && binlog_filter->db_ok(table->s->db.str) && - strcmp("mysql", table->s->db.str) != 0); + !table->no_replicate); table->s->cached_row_logging_check= check; } @@ -3476,7 +3465,7 @@ namespace { } } -/* +/** @brief Write table maps for all (manually or automatically) locked tables to the binary log. @@ -3497,14 +3486,16 @@ namespace { SEE ALSO THD::lock THD::locked_tables - */ +*/ namespace { int write_locked_table_maps(THD *thd) { DBUG_ENTER("write_locked_table_maps"); - DBUG_PRINT("enter", ("thd=%p, thd->lock=%p, thd->locked_tables=%p, thd->extra_lock", - thd, thd->lock, thd->locked_tables, thd->extra_lock)); + DBUG_PRINT("enter", ("thd: 0x%lx thd->lock: 0x%lx thd->locked_tables: 0x%lx " + "thd->extra_lock: 0x%lx", + (long) thd, (long) thd->lock, + (long) thd->locked_tables, (long) thd->extra_lock)); if (thd->get_binlog_table_maps() == 0) { @@ -3524,7 +3515,7 @@ namespace ++table_ptr) { TABLE *const table= *table_ptr; - DBUG_PRINT("info", ("Checking table %s", table->s->table_name)); + DBUG_PRINT("info", ("Checking table %s", table->s->table_name.str)); if (table->current_lock == F_WRLCK && check_table_binlog_row_based(thd, table)) { @@ -3604,7 +3595,6 @@ namespace binlog_log_row<Update_rows_log_event>(TABLE *, const byte *, const byte *); } -#endif /* HAVE_ROW_BASED_REPLICATION */ int handler::ha_external_lock(THD *thd, int lock_type) { @@ -3619,10 +3609,9 @@ int handler::ha_external_lock(THD *thd, int lock_type) } -/* +/** @brief Check handler usage and reset state of file to after 'open' */ - int handler::ha_reset() { DBUG_ENTER("ha_reset"); @@ -3645,10 +3634,8 @@ int handler::ha_write_row(byte *buf) int error; if (unlikely(error= write_row(buf))) return error; -#ifdef HAVE_ROW_BASED_REPLICATION if (unlikely(error= binlog_log_row<Write_rows_log_event>(table, 0, buf))) return error; -#endif return 0; } @@ -3664,10 +3651,8 @@ int handler::ha_update_row(const byte *old_data, byte *new_data) if (unlikely(error= update_row(old_data, new_data))) return error; -#ifdef HAVE_ROW_BASED_REPLICATION if (unlikely(error= binlog_log_row<Update_rows_log_event>(table, old_data, new_data))) return error; -#endif return 0; } @@ -3676,21 +3661,18 @@ int handler::ha_delete_row(const byte *buf) int error; if (unlikely(error= delete_row(buf))) return error; -#ifdef HAVE_ROW_BASED_REPLICATION if (unlikely(error= binlog_log_row<Delete_rows_log_event>(table, buf, 0))) return error; -#endif return 0; } -/* +/** @brief use_hidden_primary_key() is called in case of an update/delete when (table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined but we don't have a primary key */ - void handler::use_hidden_primary_key() { /* fallback to use all columns in the table to identify row */ @@ -3698,11 +3680,10 @@ void handler::use_hidden_primary_key() } -/* +/** @brief Dummy function which accept information about log files which is not need by handlers */ - void signal_log_not_needed(struct handlerton, char *log_file) { DBUG_ENTER("signal_log_not_needed"); @@ -3758,12 +3739,11 @@ err: #define fl_dir FN_ROOTDIR -/* +/** @brief Dummy function to return log status should be replaced by function which really detect the log status and check that the file is a log of this handler. */ - enum log_status fl_get_log_status(char *log) { MY_STAT stat_buff; @@ -3803,7 +3783,7 @@ void fl_log_iterator_destroy(struct handler_iterator *iterator) } -/* +/** @brief returns buffer, to be assigned in handler_iterator struct */ enum handler_create_iterator_result diff --git a/sql/handler.h b/sql/handler.h index 5e26d9c7b63..82970cc1ac6 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000,2004 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -684,6 +683,7 @@ struct handlerton int (*table_exists_in_engine)(handlerton *hton, THD* thd, const char *db, const char *name); uint32 license; /* Flag for Engine License */ + void *data; /* Location for engines to keep personal structures */ }; @@ -763,7 +763,7 @@ typedef struct st_ha_create_information bool table_existed; /* 1 in create if table existed */ bool frm_only; /* 1 if no ha_create_table() */ bool varchar; /* 1 if table has a VARCHAR */ - bool store_on_disk; /* 1 if table stored on disk */ + enum ha_storage_media storage_media; /* DEFAULT, DISK or MEMORY */ } HA_CREATE_INFO; @@ -975,6 +975,10 @@ public: thd Handler of the thread, trying to lock the table table Table handler to check count Number of locks already granted to the table + called_by_privileged_thread TRUE if called from a logger THD + (general_log_thd or slow_log_thd) + or by a privileged thread, which + has the right to lock log tables. DESCRIPTION Check whether a handler allows to lock the table. For instance, @@ -990,7 +994,7 @@ public: virtual bool check_if_locking_is_allowed(uint sql_command, ulong type, TABLE *table, uint count, - bool called_by_logger_thread) + bool called_by_privileged_thread) { return TRUE; } @@ -1243,7 +1247,7 @@ public: key_range *max_key) { return (ha_rows) 10; } virtual void position(const byte *record)=0; - virtual void info(uint)=0; // see my_base.h for full description + virtual int info(uint)=0; // see my_base.h for full description virtual void get_dynamic_partition_info(PARTITION_INFO *stat_info, uint part_id); virtual int extra(enum ha_extra_function operation) @@ -1405,7 +1409,7 @@ public: { return FALSE; } virtual char* get_foreign_key_create_info() { return(NULL);} /* gets foreign key create string from InnoDB */ - virtual char* get_tablespace_name(THD *thd) + virtual char* get_tablespace_name(THD *thd, char *name, uint name_len) { return(NULL);} /* gets tablespace name from handler */ /* used in ALTER TABLE; 1 if changing storage engine is allowed */ virtual bool can_switch_engines() { return 1; } @@ -1654,6 +1658,7 @@ static inline bool ha_storage_engine_is_enabled(const handlerton *db_type) /* basic stuff */ int ha_init(void); +int ha_end(void); int ha_initialize_handlerton(st_plugin_int *plugin); int ha_finalize_handlerton(st_plugin_int *plugin); diff --git a/sql/hash_filo.cc b/sql/hash_filo.cc index ec200768222..9303120e18a 100644 --- a/sql/hash_filo.cc +++ b/sql/hash_filo.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2001, 2005 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/hash_filo.h b/sql/hash_filo.h index fc48c3b1540..c25af67b572 100644 --- a/sql/hash_filo.h +++ b/sql/hash_filo.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2003, 2005 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/hostname.cc b/sql/hostname.cc index 52c4107372f..049220c6b6f 100644 --- a/sql/hostname.cc +++ b/sql/hostname.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/init.cc b/sql/init.cc index 9f975296cb6..ff236c03204 100644 --- a/sql/init.cc +++ b/sql/init.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2005 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -46,5 +45,11 @@ void unireg_init(ulong options) { /* It's used by filesort... */ log_10[i]= nr ; nr*= 10.0; } + /* Make a tab of powers of 0.1 */ + for (i= 0, nr= 0.1; i < array_elements(log_01); i++) + { + log_01[i]= nr; + nr*= 0.1; + } DBUG_VOID_RETURN; } diff --git a/sql/item.cc b/sql/item.cc index f8a8b4a6272..72cbc91b91e 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -26,10 +25,6 @@ #include "sql_trigger.h" #include "sql_select.h" -static void mark_as_dependent(THD *thd, - SELECT_LEX *last, SELECT_LEX *current, - Item_ident *item); - const String my_null_string("NULL", 4, default_charset_info); /****************************************************************************/ @@ -269,6 +264,32 @@ my_decimal *Item::val_decimal_from_string(my_decimal *decimal_value) } +my_decimal *Item::val_decimal_from_date(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed == 1); + TIME ltime; + if (get_date(<ime, TIME_FUZZY_DATE)) + { + my_decimal_set_zero(decimal_value); + return 0; + } + return date2my_decimal(<ime, decimal_value); +} + + +my_decimal *Item::val_decimal_from_time(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed == 1); + TIME ltime; + if (get_time(<ime)) + { + my_decimal_set_zero(decimal_value); + return 0; + } + return date2my_decimal(<ime, decimal_value); +} + + double Item::val_real_from_decimal() { /* Note that fix_fields may not be called for Item_avg_field items */ @@ -292,6 +313,25 @@ longlong Item::val_int_from_decimal() return result; } +int Item::save_time_in_field(Field *field) +{ + TIME ltime; + if (get_time(<ime)) + return set_field_to_null(field); + field->set_notnull(); + return field->store_time(<ime, MYSQL_TIMESTAMP_TIME); +} + + +int Item::save_date_in_field(Field *field) +{ + TIME ltime; + if (get_date(<ime, TIME_FUZZY_DATE)) + return set_field_to_null(field); + field->set_notnull(); + return field->store_time(<ime, MYSQL_TIMESTAMP_DATETIME); +} + Item::Item(): rsize(0), name(0), orig_name(0), name_length(0), fixed(0), @@ -1176,6 +1216,28 @@ void Item_name_const::print(String *str) /* + need a special class to adjust printing : references to aggregate functions + must not be printed as refs because the aggregate functions that are added to + the front of select list are not printed as well. +*/ +class Item_aggregate_ref : public Item_ref +{ +public: + Item_aggregate_ref(Name_resolution_context *context_arg, Item **item, + const char *table_name_arg, const char *field_name_arg) + :Item_ref(context_arg, item, table_name_arg, field_name_arg) {} + + void print (String *str) + { + if (ref) + (*ref)->print(str); + else + Item_ident::print(str); + } +}; + + +/* Move SUM items out from item tree and replace with reference SYNOPSIS @@ -1204,7 +1266,10 @@ void Item::split_sum_func2(THD *thd, Item **ref_pointer_array, if (type() == SUM_FUNC_ITEM && skip_registered && ((Item_sum *) this)->ref_by) return; - if (type() != SUM_FUNC_ITEM && with_sum_func) + if ((type() != SUM_FUNC_ITEM && with_sum_func) || + (type() == FUNC_ITEM && + (((Item_func *) this)->functype() == Item_func::ISNOTNULLTEST_FUNC || + ((Item_func *) this)->functype() == Item_func::TRIG_COND_FUNC))) { /* Will split complicated items and ignore simple ones */ split_sum_func(thd, ref_pointer_array, fields); @@ -1228,8 +1293,8 @@ void Item::split_sum_func2(THD *thd, Item **ref_pointer_array, Item *new_item, *real_itm= real_item(); ref_pointer_array[el]= real_itm; - if (!(new_item= new Item_ref(&thd->lex->current_select->context, - ref_pointer_array + el, 0, name))) + if (!(new_item= new Item_aggregate_ref(&thd->lex->current_select->context, + ref_pointer_array + el, 0, name))) return; // fatal_error is set fields.push_front(real_itm); thd->change_item_tree(ref, new_item); @@ -1473,7 +1538,7 @@ bool agg_item_collations_for_comparison(DTCollation &c, const char *fname, bool agg_item_charsets(DTCollation &coll, const char *fname, Item **args, uint nargs, uint flags, int item_sep) { - Item **arg, **last, *safe_args[2]; + Item **arg, *safe_args[2]; LINT_INIT(safe_args[0]); LINT_INIT(safe_args[1]); @@ -1488,6 +1553,8 @@ bool agg_item_charsets(DTCollation &coll, const char *fname, doesn't display each argument's characteristics. - if nargs is 1, then this error cannot happen. */ + LINT_INIT(safe_args[0]); + LINT_INIT(safe_args[1]); if (nargs >=2 && nargs <= 3) { safe_args[0]= args[0]; @@ -1572,7 +1639,7 @@ void Item_ident_for_show::make_field(Send_field *tmp_field) Item_field::Item_field(Field *f) :Item_ident(0, NullS, *f->table_name, f->field_name), item_equal(0), no_const_subst(0), - have_privileges(0), any_privileges(0) + have_privileges(0), any_privileges(0), fixed_as_field(0) { set_field(f); /* @@ -1587,7 +1654,7 @@ Item_field::Item_field(THD *thd, Name_resolution_context *context_arg, Field *f) :Item_ident(context_arg, f->table->s->db.str, *f->table_name, f->field_name), item_equal(0), no_const_subst(0), - have_privileges(0), any_privileges(0) + have_privileges(0), any_privileges(0), fixed_as_field(0) { /* We always need to provide Item_field with a fully qualified field @@ -1626,9 +1693,12 @@ Item_field::Item_field(Name_resolution_context *context_arg, const char *field_name_arg) :Item_ident(context_arg, db_arg,table_name_arg,field_name_arg), field(0), result_field(0), item_equal(0), no_const_subst(0), - have_privileges(0), any_privileges(0) + have_privileges(0), any_privileges(0), fixed_as_field(0) { + SELECT_LEX *select= current_thd->lex->current_select; collation.set(DERIVATION_IMPLICIT); + if (select && select->parsing_place != IN_HAVING) + select->select_n_where_fields++; } // Constructor need to process subselect with temporary tables (see Item) @@ -1639,7 +1709,8 @@ Item_field::Item_field(THD *thd, Item_field *item) item_equal(item->item_equal), no_const_subst(item->no_const_subst), have_privileges(item->have_privileges), - any_privileges(item->any_privileges) + any_privileges(item->any_privileges), + fixed_as_field(item->fixed_as_field) { collation.set(DERIVATION_IMPLICIT); } @@ -1649,13 +1720,13 @@ void Item_field::set_field(Field *field_par) field=result_field=field_par; // for easy coding with fields maybe_null=field->maybe_null(); decimals= field->decimals(); - max_length= field_par->max_length(); + max_length= field_par->max_display_length(); table_name= *field_par->table_name; field_name= field_par->field_name; db_name= field_par->table->s->db.str; alias_name_used= field_par->table->alias_name_used; unsigned_flag=test(field_par->flags & UNSIGNED_FLAG); - collation.set(field_par->charset(), DERIVATION_IMPLICIT); + collation.set(field_par->charset(), field_par->derivation()); fixed= 1; } @@ -1720,9 +1791,10 @@ void Item_ident::print(String *str) } } - if (!table_name || !field_name) + if (!table_name || !field_name || !field_name[0]) { - const char *nm= field_name ? field_name : name ? name : "tmp_field"; + const char *nm= (field_name && field_name[0]) ? + field_name : name ? name : "tmp_field"; append_identifier(thd, str, nm, (uint) strlen(nm)); return; } @@ -2139,12 +2211,6 @@ void Item_string::print(String *str) } -inline bool check_if_only_end_space(CHARSET_INFO *cs, char *str, char *end) -{ - return str+ cs->cset->scan(cs, str, end, MY_SEQ_SPACES) == end; -} - - double Item_string::val_real() { DBUG_ASSERT(fixed == 1); @@ -2364,21 +2430,22 @@ void Item_param::set_decimal(const char *str, ulong length) the fact that even wrong value sent over binary protocol fits into MAX_DATE_STRING_REP_LENGTH buffer. */ -void Item_param::set_time(TIME *tm, timestamp_type type, uint32 max_length_arg) +void Item_param::set_time(TIME *tm, timestamp_type time_type, + uint32 max_length_arg) { DBUG_ENTER("Item_param::set_time"); value.time= *tm; - value.time.time_type= type; + value.time.time_type= time_type; if (value.time.year > 9999 || value.time.month > 12 || value.time.day > 31 || - type != MYSQL_TIMESTAMP_TIME && value.time.hour > 23 || + time_type != MYSQL_TIMESTAMP_TIME && value.time.hour > 23 || value.time.minute > 59 || value.time.second > 59) { char buff[MAX_DATE_STRING_REP_LENGTH]; uint length= my_TIME_to_str(&value.time, buff); - make_truncated_value_warning(current_thd, buff, length, type, 0); + make_truncated_value_warning(current_thd, buff, length, time_type, 0); set_zero_time(&value.time, MYSQL_TIMESTAMP_ERROR); } @@ -2844,7 +2911,7 @@ bool Item_param::basic_const_item() const Item * -Item_param::new_item() +Item_param::clone_item() { /* see comments in the header file */ switch (state) { @@ -3288,7 +3355,7 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) ORDER *group_list= (ORDER*) select->group_list.first; bool ambiguous_fields= FALSE; uint counter; - bool not_used; + enum_resolution_type resolution; /* Search for a column or derived column named as 'ref' in the SELECT @@ -3296,8 +3363,10 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) */ if (!(select_ref= find_item_in_list(ref, *(select->get_item_list()), &counter, REPORT_EXCEPT_NOT_FOUND, - ¬_used))) + &resolution))) return NULL; /* Some error occurred. */ + if (resolution == RESOLVED_AGAINST_ALIAS) + ref->alias_name_used= TRUE; /* If this is a non-aggregated field inside HAVING, search in GROUP BY. */ if (select->having_fix_field && !ref->with_sum_func && group_list) @@ -3407,7 +3476,12 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) */ Name_resolution_context *last_checked_context= context; Item **ref= (Item **) not_found_item; - Name_resolution_context *outer_context= context->outer_context; + SELECT_LEX *current_sel= (SELECT_LEX *) thd->lex->current_select; + Name_resolution_context *outer_context= 0; + /* Currently derived tables cannot be correlated */ + if (current_sel->master_unit()->first_select()->linkage != + DERIVED_TABLE_TYPE) + outer_context= context->outer_context; for (; outer_context; outer_context= outer_context->outer_context) @@ -3443,36 +3517,85 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) { if (*from_field) { + if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && + select->cur_pos_in_select_list != UNDEF_POS) + { + /* + As this is an outer field it should be added to the list of + non aggregated fields of the outer select. + */ + marker= select->cur_pos_in_select_list; + select->non_agg_fields.push_back(this); + } if (*from_field != view_ref_found) { + prev_subselect_item->used_tables_cache|= (*from_field)->table->map; prev_subselect_item->const_item_cache= 0; + if (!last_checked_context->select_lex->having_fix_field && + !fixed_as_field) + { + Item_outer_ref *rf; + Query_arena *arena= 0, backup; + /* + Each outer field is replaced for an Item_outer_ref object. + This is done in order to get correct results when the outer + select employs a temporary table. + The original fields are saved in the inner_fields_list of the + outer select. This list is created by the following reasons: + 1. We can't add field items to the outer select list directly + because the outer select hasn't been fully fixed yet. + 2. We need a location to refer to in the Item_ref object + so the inner_fields_list is used as such temporary + reference storage. + The new Item_outer_ref object replaces the original field and is + also saved in the inner_refs_list of the outer select. Here + it is only created. It can be fixed only after the original + field has been fixed and this is done in the fix_inner_refs() + function. + */ + set_field(*from_field); + arena= thd->activate_stmt_arena_if_needed(&backup); + rf= new Item_outer_ref(context, this); + if (!rf) + { + if (arena) + thd->restore_active_arena(arena, &backup); + return -1; + } + *reference= rf; + select->inner_refs_list.push_back(rf); + if (arena) + thd->restore_active_arena(arena, &backup); + fixed_as_field= 1; + } if (thd->lex->in_sum_func && thd->lex->in_sum_func->nest_level == thd->lex->current_select->nest_level) { - Item::Type type= (*reference)->type(); + Item::Type ref_type= (*reference)->type(); set_if_bigger(thd->lex->in_sum_func->max_arg_level, select->nest_level); set_field(*from_field); fixed= 1; mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, this, - ((type == REF_ITEM || type == FIELD_ITEM) ? + ((ref_type == REF_ITEM || + ref_type == FIELD_ITEM) ? (Item_ident*) (*reference) : 0)); return 0; } } else { - Item::Type type= (*reference)->type(); + Item::Type ref_type= (*reference)->type(); prev_subselect_item->used_tables_cache|= (*reference)->used_tables(); prev_subselect_item->const_item_cache&= (*reference)->const_item(); mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, this, - ((type == REF_ITEM || type == FIELD_ITEM) ? + ((ref_type == REF_ITEM || ref_type == FIELD_ITEM) ? (Item_ident*) (*reference) : 0)); /* @@ -3549,9 +3672,9 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) *ref= NULL; // Don't call set_properties() rf= (place == IN_HAVING ? new Item_ref(context, ref, (char*) table_name, - (char*) field_name) : + (char*) field_name, alias_name_used) : new Item_direct_ref(context, ref, (char*) table_name, - (char*) field_name)); + (char*) field_name, alias_name_used)); *ref= save; if (!rf) return -1; @@ -3573,7 +3696,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) { mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, - this, this); + this, (Item_ident*)*reference); if (last_checked_context->select_lex->having_fix_field) { Item_ref *rf; @@ -3645,10 +3768,11 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) bool Item_field::fix_fields(THD *thd, Item **reference) { DBUG_ASSERT(fixed == 0); + Field *from_field= (Field *)not_found_field; + bool outer_fixed= false; + if (!field) // If field is not checked { - Field *from_field= (Field *)not_found_field; - bool outer_fixed= false; /* In case of view, find_field_in_tables() write pointer to view field expression to 'reference', i.e. it substitute that expression instead @@ -3668,15 +3792,56 @@ bool Item_field::fix_fields(THD *thd, Item **reference) if (thd->lex->current_select->is_item_list_lookup) { uint counter; - bool not_used; + enum_resolution_type resolution; Item** res= find_item_in_list(this, thd->lex->current_select->item_list, &counter, REPORT_EXCEPT_NOT_FOUND, - ¬_used); - if (res != (Item **)not_found_item && - (*res)->type() == Item::FIELD_ITEM) + &resolution); + if (!res) + return 1; + if (resolution == RESOLVED_AGAINST_ALIAS) + alias_name_used= TRUE; + if (res != (Item **)not_found_item) { - set_field((*((Item_field**)res))->field); - return 0; + if ((*res)->type() == Item::FIELD_ITEM) + { + /* + It's an Item_field referencing another Item_field in the select + list. + use the field from the Item_field in the select list and leave + the Item_field instance in place. + */ + + Field *field= (*((Item_field**)res))->field; + + if (field == NULL) + { + /* The column to which we link isn't valid. */ + my_error(ER_BAD_FIELD_ERROR, MYF(0), (*res)->name, + current_thd->where); + return(1); + } + + set_field(field); + return 0; + } + else + { + /* + It's not an Item_field in the select list so we must make a new + Item_ref to point to the Item in the select list and replace the + Item_field created by the parser with the new Item_ref. + */ + Item_ref *rf= new Item_ref(context, db_name,table_name,field_name); + if (!rf) + return 1; + thd->change_item_tree(reference, rf); + /* + Because Item_ref never substitutes itself with other items + in Item_ref::fix_fields(), we can safely use the original + pointer to it even after fix_fields() + */ + return rf->fix_fields(thd, reference) || rf->check_cols(1); + } } } if ((ret= fix_outer_field(thd, &from_field, reference)) < 0) @@ -3712,6 +3877,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) goto error; if (!ret) return FALSE; + outer_fixed= 1; } set_field(from_field); @@ -3771,6 +3937,13 @@ bool Item_field::fix_fields(THD *thd, Item **reference) } #endif fixed= 1; + if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && + !outer_fixed && !thd->lex->in_sum_func && + thd->lex->current_select->cur_pos_in_select_list != UNDEF_POS) + { + thd->lex->current_select->non_agg_fields.push_back(this); + marker= thd->lex->current_select->cur_pos_in_select_list; + } return FALSE; error: @@ -3978,7 +4151,7 @@ Item *Item_field::replace_equal_field(byte *arg) void Item::init_make_field(Send_field *tmp_field, - enum enum_field_types field_type) + enum enum_field_types field_type_arg) { char *empty_name= (char*) ""; tmp_field->db_name= empty_name; @@ -3990,7 +4163,7 @@ void Item::init_make_field(Send_field *tmp_field, tmp_field->flags= (maybe_null ? 0 : NOT_NULL_FLAG) | (my_binary_compare(collation.collation) ? BINARY_FLAG : 0); - tmp_field->type=field_type; + tmp_field->type= field_type_arg; tmp_field->length=max_length; tmp_field->decimals=decimals; if (unsigned_flag) @@ -4005,12 +4178,12 @@ void Item::make_field(Send_field *tmp_field) enum_field_types Item::string_field_type() const { - enum_field_types type= FIELD_TYPE_VAR_STRING; + enum_field_types f_type= MYSQL_TYPE_VAR_STRING; if (max_length >= 16777216) - type= FIELD_TYPE_LONG_BLOB; + f_type= MYSQL_TYPE_LONG_BLOB; else if (max_length >= 65536) - type= FIELD_TYPE_MEDIUM_BLOB; - return type; + f_type= MYSQL_TYPE_MEDIUM_BLOB; + return f_type; } @@ -4024,9 +4197,9 @@ enum_field_types Item::field_type() const { switch (result_type()) { case STRING_RESULT: return string_field_type(); - case INT_RESULT: return FIELD_TYPE_LONGLONG; - case DECIMAL_RESULT: return FIELD_TYPE_NEWDECIMAL; - case REAL_RESULT: return FIELD_TYPE_DOUBLE; + case INT_RESULT: return MYSQL_TYPE_LONGLONG; + case DECIMAL_RESULT: return MYSQL_TYPE_NEWDECIMAL; + case REAL_RESULT: return MYSQL_TYPE_DOUBLE; case ROW_RESULT: default: DBUG_ASSERT(0); @@ -4193,6 +4366,10 @@ void Item_field::make_field(Send_field *tmp_field) DBUG_ASSERT(tmp_field->table_name != 0); if (name) tmp_field->col_name=name; // Use user supplied name + if (table_name) + tmp_field->table_name= table_name; + if (db_name) + tmp_field->db_name= db_name; } @@ -4217,18 +4394,19 @@ void Item_field::save_org_in_field(Field *to) int Item_field::save_in_field(Field *to, bool no_conversions) { + int res; if (result_field->is_null()) { null_value=1; - return set_field_to_null_with_conversions(to, no_conversions); + res= set_field_to_null_with_conversions(to, no_conversions); } else { to->set_notnull(); - field_conv(to,result_field); + res= field_conv(to,result_field); null_value=0; } - return 0; + return res; } @@ -4376,7 +4554,7 @@ bool Item_int::eq(const Item *arg, bool binary_cmp) const } -Item *Item_int_with_ref::new_item() +Item *Item_int_with_ref::clone_item() { DBUG_ASSERT(ref->const_item()); /* @@ -4547,18 +4725,31 @@ my_decimal *Item_hex_string::val_decimal(my_decimal *decimal_value) int Item_hex_string::save_in_field(Field *field, bool no_conversions) { - int error; field->set_notnull(); if (field->result_type() == STRING_RESULT) + return field->store(str_value.ptr(), str_value.length(), + collation.collation); + + ulonglong nr; + uint32 length= str_value.length(); + if (length > 8) { - error=field->store(str_value.ptr(),str_value.length(),collation.collation); + nr= field->flags & UNSIGNED_FLAG ? ULONGLONG_MAX : LONGLONG_MAX; + goto warn; } - else + nr= (ulonglong) val_int(); + if ((length == 8) && !(field->flags & UNSIGNED_FLAG) && (nr > LONGLONG_MAX)) { - longlong nr=val_int(); - error=field->store(nr, TRUE); // Assume hex numbers are unsigned + nr= LONGLONG_MAX; + goto warn; } - return error; + return field->store((longlong) nr, TRUE); // Assume hex numbers are unsigned + +warn: + if (!field->store((longlong) nr, TRUE)) + field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, + 1); + return 1; } @@ -4641,10 +4832,10 @@ bool Item_null::send(Protocol *protocol, String *packet) bool Item::send(Protocol *protocol, String *buffer) { bool result; - enum_field_types type; + enum_field_types f_type; LINT_INIT(result); // Will be set if null_value == 0 - switch ((type=field_type())) { + switch ((f_type=field_type())) { default: case MYSQL_TYPE_NULL: case MYSQL_TYPE_DECIMAL: @@ -4723,7 +4914,7 @@ bool Item::send(Protocol *protocol, String *buffer) get_date(&tm, TIME_FUZZY_DATE); if (!null_value) { - if (type == MYSQL_TYPE_DATE) + if (f_type == MYSQL_TYPE_DATE) return protocol->store_date(&tm); else result= protocol->store(&tm); @@ -4751,17 +4942,95 @@ bool Item_field::send(Protocol *protocol, String *buffer) } +void Item_field::update_null_value() +{ + /* + need to set no_errors to prevent warnings about type conversion + popping up. + */ + THD *thd= field->table->in_use; + int no_errors; + + no_errors= thd->no_errors; + thd->no_errors= 1; + Item::update_null_value(); + thd->no_errors= no_errors; +} + + +/* + Add the field to the select list and substitute it for the reference to + the field. + + SYNOPSIS + Item_field::update_value_transformer() + select_arg current select + + DESCRIPTION + If the field doesn't belong to the table being inserted into then it is + added to the select list, pointer to it is stored in the ref_pointer_array + of the select and the field itself is substituted for the Item_ref object. + This is done in order to get correct values from update fields that + belongs to the SELECT part in the INSERT .. SELECT .. ON DUPLICATE KEY + UPDATE statement. + + RETURN + 0 if error occured + ref if all conditions are met + this field otherwise +*/ + +Item *Item_field::update_value_transformer(byte *select_arg) +{ + SELECT_LEX *select= (SELECT_LEX*)select_arg; + DBUG_ASSERT(fixed); + + if (field->table != select->context.table_list->table && + type() != Item::TRIGGER_FIELD_ITEM) + { + List<Item> *all_fields= &select->join->all_fields; + Item **ref_pointer_array= select->ref_pointer_array; + int el= all_fields->elements; + Item_ref *ref; + + ref_pointer_array[el]= (Item*)this; + all_fields->push_front((Item*)this); + ref= new Item_ref(&select->context, ref_pointer_array + el, + table_name, field_name); + return ref; + } + return this; +} + + +void Item_field::print(String *str) +{ + if (field && field->table->const_table) + { + char buff[MAX_FIELD_WIDTH]; + String tmp(buff,sizeof(buff),str->charset()); + field->val_str(&tmp); + str->append('\''); + str->append(tmp); + str->append('\''); + return; + } + Item_ident::print(str); +} + + Item_ref::Item_ref(Name_resolution_context *context_arg, Item **item, const char *table_name_arg, - const char *field_name_arg) + const char *field_name_arg, + bool alias_name_used_arg) :Item_ident(context_arg, NullS, table_name_arg, field_name_arg), result_field(0), ref(item) { + alias_name_used= alias_name_used_arg; /* This constructor used to create some internals references over fixed items */ - DBUG_ASSERT(ref != 0); - if (*ref && (*ref)->fixed) + if (ref && *ref && (*ref)->fixed) set_properties(); } @@ -4920,7 +5189,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) goto error; if (from_field == view_ref_found) { - Item::Type type= (*reference)->type(); + Item::Type refer_type= (*reference)->type(); prev_subselect_item->used_tables_cache|= (*reference)->used_tables(); prev_subselect_item->const_item_cache&= @@ -4928,7 +5197,8 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) DBUG_ASSERT((*reference)->type() == REF_ITEM); mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, this, - ((type == REF_ITEM || type == FIELD_ITEM) ? + ((refer_type == REF_ITEM || + refer_type == FIELD_ITEM) ? (Item_ident*) (*reference) : 0)); /* @@ -5039,11 +5309,13 @@ void Item_ref::set_properties() */ with_sum_func= (*ref)->with_sum_func; unsigned_flag= (*ref)->unsigned_flag; + fixed= 1; + if (alias_name_used) + return; if ((*ref)->type() == FIELD_ITEM) alias_name_used= ((Item_ident *) (*ref))->alias_name_used; else alias_name_used= TRUE; // it is not field, so it is was resolved by alias - fixed= 1; } @@ -5061,7 +5333,7 @@ void Item_ref::print(String *str) if (ref) { if ((*ref)->type() != Item::CACHE_ITEM && ref_type() != VIEW_REF && - name && alias_name_used) + !table_name && name && alias_name_used) { THD *thd= current_thd; append_identifier(thd, str, name, (uint) strlen(name)); @@ -5219,18 +5491,7 @@ my_decimal *Item_ref::val_decimal(my_decimal *decimal_value) int Item_ref::save_in_field(Field *to, bool no_conversions) { int res; - if (result_field) - { - if (result_field->is_null()) - { - null_value= 1; - return set_field_to_null_with_conversions(to, no_conversions); - } - to->set_notnull(); - field_conv(to, result_field); - null_value= 0; - return 0; - } + DBUG_ASSERT(!result_field); res= (*ref)->save_in_field(to, no_conversions); null_value= (*ref)->null_value; return res; @@ -5320,7 +5581,7 @@ bool Item_direct_ref::get_date(TIME *ltime,uint fuzzydate) /* - Prepare referenced view viewld then call usual Item_direct_ref::fix_fields + Prepare referenced field then call usual Item_direct_ref::fix_fields SYNOPSIS Item_direct_view_ref::fix_fields() @@ -5344,6 +5605,31 @@ bool Item_direct_view_ref::fix_fields(THD *thd, Item **reference) } /* + Prepare referenced outer field then call usual Item_direct_ref::fix_fields + + SYNOPSIS + Item_outer_ref::fix_fields() + thd thread handler + reference reference on reference where this item stored + + RETURN + FALSE OK + TRUE Error +*/ + +bool Item_outer_ref::fix_fields(THD *thd, Item **reference) +{ + DBUG_ASSERT(*ref); + /* outer_field->check_cols() will be made in Item_direct_ref::fix_fields */ + outer_field->fixed_as_field= 1; + if (!outer_field->fixed && + (outer_field->fix_fields(thd, reference))) + return TRUE; + table_name= outer_field->table_name; + return Item_direct_ref::fix_fields(thd, reference); +} + +/* Compare two view column references for equality. SYNOPSIS @@ -5449,6 +5735,13 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions) { if (field_arg->flags & NO_DEFAULT_VALUE_FLAG) { + if (field_arg->reset()) + { + my_message(ER_CANT_CREATE_GEOMETRY_OBJECT, + ER(ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0)); + return -1; + } + if (context->error_processor == &view_error_processor) { TABLE_LIST *view= cached_table->top_table(); @@ -5640,7 +5933,7 @@ void Item_trigger_field::set_required_privilege(bool rw) } -bool Item_trigger_field::set_value(THD *thd, sp_rcontext */*ctx*/, Item **it) +bool Item_trigger_field::set_value(THD *thd, sp_rcontext * /*ctx*/, Item **it) { Item *item= sp_prepare_func_item(thd, it); @@ -5673,8 +5966,8 @@ bool Item_trigger_field::fix_fields(THD *thd, Item **items) { table_grants->want_privilege= want_privilege; - if (check_grant_column(thd, table_grants, triggers->table->s->db.str, - triggers->table->s->table_name.str, field_name, + if (check_grant_column(thd, table_grants, triggers->trigger_table->s->db.str, + triggers->trigger_table->s->table_name.str, field_name, strlen(field_name), thd->security_ctx)) return TRUE; } @@ -5787,7 +6080,8 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) DBUG_ASSERT(item_row->cols() == comp_item_row->cols()); col= item_row->cols(); while (col-- > 0) - resolve_const_item(thd, item_row->addr(col), comp_item_row->el(col)); + resolve_const_item(thd, item_row->addr(col), + comp_item_row->element_index(col)); break; } /* Fallthrough */ @@ -6055,7 +6349,7 @@ bool Item_cache_row::setup(Item * item) return 1; for (uint i= 0; i < item_count; i++) { - Item *el= item->el(i); + Item *el= item->element_index(i); Item_cache *tmp; if (!(tmp= values[i]= Item_cache::get_cache(el->result_type()))) return 1; @@ -6071,7 +6365,7 @@ void Item_cache_row::store(Item * item) item->bring_value(); for (uint i= 0; i < item_count; i++) { - values[i]->store(item->el(i)); + values[i]->store(item->element_index(i)); null_value|= values[i]->null_value; } } @@ -6109,7 +6403,7 @@ bool Item_cache_row::null_inside() } else { - values[i]->val_int(); + values[i]->update_null_value(); if (values[i]->null_value) return 1; } diff --git a/sql/item.h b/sql/item.h index 8799fa07eb7..dfe8842dfbb 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000-2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -27,19 +26,7 @@ class Item_field; /* "Declared Type Collation" A combination of collation and its derivation. -*/ - -enum Derivation -{ - DERIVATION_IGNORABLE= 5, - DERIVATION_COERCIBLE= 4, - DERIVATION_SYSCONST= 3, - DERIVATION_IMPLICIT= 2, - DERIVATION_NONE= 1, - DERIVATION_EXPLICIT= 0 -}; -/* Flags for collation aggregation modes: MY_COLL_ALLOW_SUPERSET_CONV - allow conversion to a superset MY_COLL_ALLOW_COERCIBLE_CONV - allow conversion of a coercible value @@ -338,10 +325,10 @@ private: TABLE_LIST *save_first_name_resolution_table; TABLE_LIST *save_next_name_resolution_table; bool save_resolve_in_select_list; + TABLE_LIST *save_next_local; public: Name_resolution_context_state() {} /* Remove gcc warning */ - TABLE_LIST *save_next_local; public: /* Save the state of a name resolution context. */ @@ -349,25 +336,25 @@ public: { save_table_list= context->table_list; save_first_name_resolution_table= context->first_name_resolution_table; - save_next_name_resolution_table= (context->first_name_resolution_table) ? - context->first_name_resolution_table-> - next_name_resolution_table : - NULL; save_resolve_in_select_list= context->resolve_in_select_list; save_next_local= table_list->next_local; + save_next_name_resolution_table= table_list->next_name_resolution_table; } /* Restore a name resolution context from saved state. */ void restore_state(Name_resolution_context *context, TABLE_LIST *table_list) { table_list->next_local= save_next_local; + table_list->next_name_resolution_table= save_next_name_resolution_table; context->table_list= save_table_list; context->first_name_resolution_table= save_first_name_resolution_table; - if (context->first_name_resolution_table) - context->first_name_resolution_table-> - next_name_resolution_table= save_next_name_resolution_table; context->resolve_in_select_list= save_resolve_in_select_list; } + + TABLE_LIST *get_first_name_resolution_table() + { + return save_first_name_resolution_table; + } }; @@ -495,7 +482,8 @@ public: Item *next; uint32 max_length; uint name_length; /* Length of name */ - uint8 marker, decimals; + int8 marker; + uint8 decimals; my_bool maybe_null; /* If item may be null */ my_bool null_value; /* if item is null */ my_bool unsigned_flag; @@ -658,9 +646,14 @@ public: my_decimal *val_decimal_from_real(my_decimal *decimal_value); my_decimal *val_decimal_from_int(my_decimal *decimal_value); my_decimal *val_decimal_from_string(my_decimal *decimal_value); + my_decimal *val_decimal_from_date(my_decimal *decimal_value); + my_decimal *val_decimal_from_time(my_decimal *decimal_value); longlong val_int_from_decimal(); double val_real_from_decimal(); + int save_time_in_field(Field *field); + int save_date_in_field(Field *field); + virtual Field *get_tmp_table_field() { return 0; } /* This is also used to create fields in CREATE ... SELECT: */ virtual Field *tmp_table_field(TABLE *t_arg) { return 0; } @@ -699,7 +692,7 @@ public: */ virtual bool basic_const_item() const { return 0; } /* cloning of constant items (0 if it is not const) */ - virtual Item *new_item() { return 0; } + virtual Item *clone_item() { return 0; } virtual cond_result eq_cmp_result() const { return COND_OK; } inline uint float_length(uint decimals_par) const { return decimals != NOT_FIXED_DEC ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;} @@ -752,6 +745,11 @@ public: virtual bool is_null() { return 0; } /* + Make sure the null_value member has a correct value. + */ + virtual void update_null_value () { (void) val_int(); } + + /* Inform the item that there will be no distinction between its result being FALSE or NULL. @@ -825,6 +823,7 @@ public: virtual bool collect_item_field_processor(byte * arg) { return 0; } virtual bool find_item_in_field_list_processor(byte *arg) { return 0; } virtual bool change_context_processor(byte *context) { return 0; } + virtual bool reset_query_id_processor(byte *query_id_arg) { return 0; } virtual bool is_expensive_processor(byte *arg) { return 0; } virtual bool register_field_in_read_map(byte *arg) { return 0; } /* @@ -902,11 +901,11 @@ public: For SP local variable returns address of pointer to Item representing its current value and pointer passed via parameter otherwise. */ - virtual Item **this_item_addr(THD *thd, Item **addr) { return addr; } + virtual Item **this_item_addr(THD *thd, Item **addr_arg) { return addr_arg; } // Row emulation virtual uint cols() { return 1; } - virtual Item* el(uint i) { return this; } + virtual Item* element_index(uint i) { return this; } virtual Item** addr(uint i) { return 0; } virtual bool check_cols(uint c); // It is not row => null inside is impossible @@ -918,6 +917,7 @@ public: virtual Item_field *filed_for_view_update() { return 0; } virtual Item *neg_transformer(THD *thd) { return NULL; } + virtual Item *update_value_transformer(byte *select_arg) { return this; } virtual Item *safe_charset_converter(CHARSET_INFO *tocs); void delete_self() { @@ -1172,7 +1172,8 @@ class Item_name_const : public Item Item *value_item; Item *name_item; public: - Item_name_const(Item *name, Item *val): value_item(val), name_item(name) + Item_name_const(Item *name_arg, Item *val): + value_item(val), name_item(name_arg) { Item::maybe_null= TRUE; } @@ -1316,7 +1317,7 @@ public: uint have_privileges; /* field need any privileges (for VIEW creation) */ bool any_privileges; - + bool fixed_as_field; Item_field(Name_resolution_context *context_arg, const char *db_arg,const char *table_name_arg, const char *field_name_arg); @@ -1376,6 +1377,7 @@ public: bool get_date_result(TIME *ltime,uint fuzzydate); bool get_time(TIME *ltime); bool is_null() { return field->is_null(); } + void update_null_value(); Item *get_tmp_table_item(THD *thd); bool collect_item_field_processor(byte * arg); bool find_item_in_field_list_processor(byte *arg); @@ -1391,10 +1393,12 @@ public: Item *equal_fields_propagator(byte *arg); bool set_no_const_sub(byte *arg); Item *replace_equal_field(byte *arg); - inline uint32 max_disp_length() { return field->max_length(); } + inline uint32 max_disp_length() { return field->max_display_length(); } Item_field *filed_for_view_update() { return this; } Item *safe_charset_converter(CHARSET_INFO *tocs); int fix_outer_field(THD *thd, Field **field, Item **reference); + virtual Item *update_value_transformer(byte *select_arg); + void print(String *str); friend class Item_default_value; friend class Item_insert_value; friend class st_select_lex_unit; @@ -1425,7 +1429,7 @@ public: /* to prevent drop fixed flag (no need parent cleanup call) */ void cleanup() {} bool basic_const_item() const { return 1; } - Item *new_item() { return new Item_null(name); } + Item *clone_item() { return new Item_null(name); } bool is_null() { return 1; } void print(String *str) { str->append(STRING_WITH_LEN("NULL")); } Item *safe_charset_converter(CHARSET_INFO *tocs); @@ -1574,7 +1578,7 @@ public: basic_const_item returned TRUE. */ Item *safe_charset_converter(CHARSET_INFO *tocs); - Item *new_item(); + Item *clone_item(); /* Implement by-value equality evaluation if parameter value is set and is a basic constant (integer, real or string). @@ -1606,7 +1610,7 @@ public: String *val_str(String*); int save_in_field(Field *field, bool no_conversions); bool basic_const_item() const { return 1; } - Item *new_item() { return new Item_int(name,value,max_length); } + Item *clone_item() { return new Item_int(name,value,max_length); } // to prevent drop fixed flag (no need parent cleanup call) void cleanup() {} void print(String *str); @@ -1626,7 +1630,7 @@ public: double val_real() { DBUG_ASSERT(fixed == 1); return ulonglong2double((ulonglong)value); } String *val_str(String*); - Item *new_item() { return new Item_uint(name,max_length); } + Item *clone_item() { return new Item_uint(name,max_length); } int save_in_field(Field *field, bool no_conversions); void print(String *str); Item_num *neg (); @@ -1657,7 +1661,7 @@ public: my_decimal *val_decimal(my_decimal *val) { return &decimal_value; } int save_in_field(Field *field, bool no_conversions); bool basic_const_item() const { return 1; } - Item *new_item() + Item *clone_item() { return new Item_decimal(name, &decimal_value, decimals, max_length); } @@ -1715,7 +1719,7 @@ public: bool basic_const_item() const { return 1; } // to prevent drop fixed flag (no need parent cleanup call) void cleanup() {} - Item *new_item() + Item *clone_item() { return new Item_float(name, value, decimals, max_length); } Item_num *neg() { value= -value; return this; } void print(String *str); @@ -1801,7 +1805,7 @@ public: enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } bool basic_const_item() const { return 1; } bool eq(const Item *item, bool binary_cmp) const; - Item *new_item() + Item *clone_item() { return new Item_string(name, str_value.ptr(), str_value.length(), collation.collation); @@ -1855,9 +1859,9 @@ class Item_return_int :public Item_int { enum_field_types int_field_type; public: - Item_return_int(const char *name, uint length, + Item_return_int(const char *name_arg, uint length, enum_field_types field_type_arg) - :Item_int(name, 0, length), int_field_type(field_type_arg) + :Item_int(name_arg, 0, length), int_field_type(field_type_arg) { unsigned_flag=1; } @@ -1873,7 +1877,10 @@ public: Item_hex_string(const char *str,uint str_length); enum Type type() const { return VARBIN_ITEM; } double val_real() - { DBUG_ASSERT(fixed == 1); return (double) Item_hex_string::val_int(); } + { + DBUG_ASSERT(fixed == 1); + return (double) (ulonglong) Item_hex_string::val_int(); + } longlong val_int(); bool basic_const_item() const { return 1; } String *val_str(String*) { DBUG_ASSERT(fixed == 1); return &str_value; } @@ -1925,7 +1932,7 @@ class Item_ref :public Item_ident protected: void set_properties(); public: - enum Ref_Type { REF, DIRECT_REF, VIEW_REF }; + enum Ref_Type { REF, DIRECT_REF, VIEW_REF, OUTER_REF }; Field *result_field; /* Save result here */ Item **ref; Item_ref(Name_resolution_context *context_arg, @@ -1948,7 +1955,8 @@ public: with Bar, and if we have a more broader set of problems like this. */ Item_ref(Name_resolution_context *context_arg, Item **item, - const char *table_name_arg, const char *field_name_arg); + const char *table_name_arg, const char *field_name_arg, + bool alias_name_used_arg= FALSE); /* Constructor need to process subselect with temporary tables (see Item) */ Item_ref(THD *thd, Item_ref *item) @@ -1986,7 +1994,7 @@ public: (*ref)->get_tmp_table_item(thd)); } table_map used_tables() const - { + { return depended_from ? OUTER_REF_TABLE_BIT : (*ref)->used_tables(); } table_map not_null_tables() const { return (*ref)->not_null_tables(); } @@ -2023,8 +2031,11 @@ class Item_direct_ref :public Item_ref public: Item_direct_ref(Name_resolution_context *context_arg, Item **item, const char *table_name_arg, - const char *field_name_arg) - :Item_ref(context_arg, item, table_name_arg, field_name_arg) {} + const char *field_name_arg, + bool alias_name_used_arg= FALSE) + :Item_ref(context_arg, item, table_name_arg, + field_name_arg, alias_name_used_arg) + {} /* Constructor need to process subselect with temporary tables (see Item) */ Item_direct_ref(THD *thd, Item_direct_ref *item) : Item_ref(thd, item) {} @@ -2059,8 +2070,52 @@ public: }; +class Item_outer_ref :public Item_direct_ref +{ +public: + Item_field *outer_field; + Item_outer_ref(Name_resolution_context *context_arg, + Item_field *outer_field_arg) + :Item_direct_ref(context_arg, 0, outer_field_arg->table_name, + outer_field_arg->field_name), + outer_field(outer_field_arg) + { + ref= (Item**)&outer_field; + set_properties(); + fixed= 0; + } + void cleanup() + { + ref= (Item**)&outer_field; + fixed= 0; + Item_direct_ref::cleanup(); + outer_field->cleanup(); + } + void save_in_result_field(bool no_conversions) + { + outer_field->save_org_in_field(result_field); + } + bool fix_fields(THD *, Item **); + table_map used_tables() const + { + return (*ref)->const_item() ? 0 : OUTER_REF_TABLE_BIT; + } + virtual Ref_Type ref_type() { return OUTER_REF; } +}; + + class Item_in_subselect; + +/* + An object of this class: + - Converts val_XXX() calls to ref->val_XXX_result() calls, like Item_ref. + - Sets owner->was_null=TRUE if it has returned a NULL value from any + val_XXX() function. This allows to inject an Item_ref_null_helper + object into subquery and then check if the subquery has produced a row + with NULL value. +*/ + class Item_ref_null_helper: public Item_ref { protected: @@ -2111,7 +2166,7 @@ public: { return ref->save_in_field(field, no_conversions); } - Item *new_item(); + Item *clone_item(); virtual Item *real_item() { return ref; } bool check_partition_func_processor(byte *int_arg) {return TRUE;} }; @@ -2126,7 +2181,6 @@ public: #include "item_strfunc.h" #include "item_geofunc.h" #include "item_timefunc.h" -#include "item_uniq.h" #include "item_subselect.h" #include "item_xmlfunc.h" #endif @@ -2365,6 +2419,9 @@ public: bool fix_fields(THD *, Item **); void print(String *str); table_map used_tables() const { return (table_map)0L; } + Field *get_tmp_table_field() { return 0; } + Item *copy_or_same(THD *thd) { return this; } + Item *get_tmp_table_item(THD *thd) { return copy_or_same(thd); } void cleanup(); private: @@ -2547,8 +2604,8 @@ public: enum Item_result result_type() const { return ROW_RESULT; } uint cols() { return item_count; } - Item* el(uint i) { return values[i]; } - Item** addr(uint i) { return (Item **) (values + i); } + Item *element_index(uint i) { return values[i]; } + Item **addr(uint i) { return (Item **) (values + i); } bool check_cols(uint c); bool null_inside(); void bring_value(); diff --git a/sql/item_buff.cc b/sql/item_buff.cc index 37f9ca7ce6c..c162b84f457 100644 --- a/sql/item_buff.cc +++ b/sql/item_buff.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 9435b3767a0..7e5df8fdbbf 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000-2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -27,13 +26,17 @@ static bool convert_constant_item(THD *thd, Field *field, Item **item); -static Item_result item_store_type(Item_result a,Item_result b) +static Item_result item_store_type(Item_result a, Item *item, + my_bool unsigned_flag) { + Item_result b= item->result_type(); + if (a == STRING_RESULT || b == STRING_RESULT) return STRING_RESULT; else if (a == REAL_RESULT || b == REAL_RESULT) return REAL_RESULT; - else if (a == DECIMAL_RESULT || b == DECIMAL_RESULT) + else if (a == DECIMAL_RESULT || b == DECIMAL_RESULT || + unsigned_flag != item->unsigned_flag) return DECIMAL_RESULT; else return INT_RESULT; @@ -42,6 +45,7 @@ static Item_result item_store_type(Item_result a,Item_result b) static void agg_result_type(Item_result *type, Item **items, uint nitems) { Item **item, **item_end; + my_bool unsigned_flag= 0; *type= STRING_RESULT; /* Skip beginning NULL items */ @@ -50,6 +54,7 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems) if ((*item)->type() != Item::NULL_ITEM) { *type= (*item)->result_type(); + unsigned_flag= (*item)->unsigned_flag; item++; break; } @@ -58,7 +63,7 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems) for (; item < item_end; item++) { if ((*item)->type() != Item::NULL_ITEM) - *type= item_store_type(type[0], (*item)->result_type()); + *type= item_store_type(*type, *item, unsigned_flag); } } @@ -177,6 +182,22 @@ longlong Item_func_not::val_int() } /* + We put any NOT expression into parenthesis to avoid + possible problems with internal view representations where + any '!' is converted to NOT. It may cause a problem if + '!' is used in an expression together with other operators + whose precedence is lower than the precedence of '!' yet + higher than the precedence of NOT. +*/ + +void Item_func_not::print(String *str) +{ + str->append('('); + Item_func::print(str); + str->append(')'); +} + +/* special NOT for ALL subquery */ @@ -275,6 +296,9 @@ static bool convert_constant_item(THD *thd, Field *field, Item **item) my_bitmap_map *old_write_map; my_bitmap_map *old_read_map; + LINT_INIT(old_write_map); + LINT_INIT(old_read_map); + if (table) { old_write_map= dbug_tmp_use_all_columns(table, table->write_set); @@ -304,7 +328,7 @@ static bool convert_constant_item(THD *thd, Field *field, Item **item) void Item_bool_func2::fix_length_and_dec() { max_length= 1; // Function returns 0 or 1 - THD *thd= current_thd; + THD *thd; /* As some compare functions are generated after sql_yacc, @@ -342,12 +366,13 @@ void Item_bool_func2::fix_length_and_dec() return; } + thd= current_thd; if (!thd->is_context_analysis_only()) { - Item *real_item= args[0]->real_item(); - if (real_item->type() == FIELD_ITEM) + Item *arg_real_item= args[0]->real_item(); + if (arg_real_item->type() == FIELD_ITEM) { - Field *field=((Item_field*) real_item)->field; + Field *field=((Item_field*) arg_real_item)->field; if (field->can_be_compared_as_longlong()) { if (convert_constant_item(thd, field,&args[1])) @@ -359,10 +384,10 @@ void Item_bool_func2::fix_length_and_dec() } } } - real_item= args[1]->real_item(); - if (real_item->type() == FIELD_ITEM /* && !real_item->const_item() */) + arg_real_item= args[1]->real_item(); + if (arg_real_item->type() == FIELD_ITEM) { - Field *field=((Item_field*) real_item)->field; + Field *field=((Item_field*) arg_real_item)->field; if (field->can_be_compared_as_longlong()) { if (convert_constant_item(thd, field,&args[0])) @@ -398,9 +423,9 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) return 1; for (uint i=0; i < n; i++) { - if ((*a)->el(i)->cols() != (*b)->el(i)->cols()) + if ((*a)->element_index(i)->cols() != (*b)->element_index(i)->cols()) { - my_error(ER_OPERAND_COLUMNS, MYF(0), (*a)->el(i)->cols()); + my_error(ER_OPERAND_COLUMNS, MYF(0), (*a)->element_index(i)->cols()); return 1; } comparators[i].set_cmp_func(owner, (*a)->addr(i), (*b)->addr(i)); @@ -462,8 +487,19 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) break; } case DECIMAL_RESULT: + break; case REAL_RESULT: + { + if ((*a)->decimals < NOT_FIXED_DEC && (*b)->decimals < NOT_FIXED_DEC) + { + precision= 5 * log_01[max((*a)->decimals, (*b)->decimals)]; + if (func == &Arg_comparator::compare_real) + func= &Arg_comparator::compare_real_fixed; + else if (func == &Arg_comparator::compare_e_real) + func= &Arg_comparator::compare_e_real_fixed; + } break; + } default: DBUG_ASSERT(0); } @@ -602,6 +638,44 @@ int Arg_comparator::compare_e_decimal() return test(my_decimal_cmp(val1, val2) == 0); } + +int Arg_comparator::compare_real_fixed() +{ + /* + Fix yet another manifestation of Bug#2338. 'Volatile' will instruct + gcc to flush double values out of 80-bit Intel FPU registers before + performing the comparison. + */ + volatile double val1, val2; + val1= (*a)->val_real(); + if (!(*a)->null_value) + { + val2= (*b)->val_real(); + if (!(*b)->null_value) + { + owner->null_value= 0; + if (val1 == val2 || fabs(val1 - val2) < precision) + return 0; + if (val1 < val2) + return -1; + return 1; + } + } + owner->null_value= 1; + return -1; +} + + +int Arg_comparator::compare_e_real_fixed() +{ + double val1= (*a)->val_real(); + double val2= (*b)->val_real(); + if ((*a)->null_value || (*b)->null_value) + return test((*a)->null_value && (*b)->null_value); + return test(val1 == val2 || fabs(val1 - val2) < precision); +} + + int Arg_comparator::compare_int_signed() { longlong val1= (*a)->val_int(); @@ -766,6 +840,59 @@ int Arg_comparator::compare_e_row() } +void Item_func_truth::fix_length_and_dec() +{ + maybe_null= 0; + null_value= 0; + decimals= 0; + max_length= 1; +} + + +void Item_func_truth::print(String *str) +{ + str->append('('); + args[0]->print(str); + str->append(STRING_WITH_LEN(" is ")); + if (! affirmative) + str->append(STRING_WITH_LEN("not ")); + if (value) + str->append(STRING_WITH_LEN("true")); + else + str->append(STRING_WITH_LEN("false")); + str->append(')'); +} + + +bool Item_func_truth::val_bool() +{ + bool val= args[0]->val_bool(); + if (args[0]->null_value) + { + /* + NULL val IS {TRUE, FALSE} --> FALSE + NULL val IS NOT {TRUE, FALSE} --> TRUE + */ + return (! affirmative); + } + + if (affirmative) + { + /* {TRUE, FALSE} val IS {TRUE, FALSE} value */ + return (val == value); + } + + /* {TRUE, FALSE} val IS NOT {TRUE, FALSE} value */ + return (val != value); +} + + +longlong Item_func_truth::val_int() +{ + return (val_bool() ? 1 : 0); +} + + bool Item_in_optimizer::fix_left(THD *thd, Item **ref) { if (!args[0]->fixed && args[0]->fix_fields(thd, args) || @@ -785,16 +912,17 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref) uint n= cache->cols(); for (uint i= 0; i < n; i++) { - if (args[0]->el(i)->used_tables()) - ((Item_cache *)cache->el(i))->set_used_tables(OUTER_REF_TABLE_BIT); + if (args[0]->element_index(i)->used_tables()) + ((Item_cache *)cache->element_index(i))->set_used_tables(OUTER_REF_TABLE_BIT); else - ((Item_cache *)cache->el(i))->set_used_tables(0); + ((Item_cache *)cache->element_index(i))->set_used_tables(0); } used_tables_cache= args[0]->used_tables(); } not_null_tables_cache= args[0]->not_null_tables(); with_sum_func= args[0]->with_sum_func; - const_item_cache= args[0]->const_item(); + if ((const_item_cache= args[0]->const_item())) + cache->store(args[0]); return 0; } @@ -828,14 +956,71 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref) longlong Item_in_optimizer::val_int() { + bool tmp; DBUG_ASSERT(fixed == 1); cache->store(args[0]); + if (cache->null_value) { - null_value= 1; + if (((Item_in_subselect*)args[1])->is_top_level_item()) + { + /* + We're evaluating "NULL IN (SELECT ...)". The result can be NULL or + FALSE, and we can return one instead of another. Just return NULL. + */ + null_value= 1; + } + else + { + if (!((Item_in_subselect*)args[1])->is_correlated && + result_for_null_param != UNKNOWN) + { + /* Use cached value from previous execution */ + null_value= result_for_null_param; + } + else + { + /* + We're evaluating "NULL IN (SELECT ...)". The result is: + FALSE if SELECT produces an empty set, or + NULL otherwise. + We disable the predicates we've pushed down into subselect, run the + subselect and see if it has produced any rows. + */ + Item_in_subselect *item_subs=(Item_in_subselect*)args[1]; + if (cache->cols() == 1) + { + item_subs->set_cond_guard_var(0, FALSE); + (void) args[1]->val_bool_result(); + result_for_null_param= null_value= !item_subs->engine->no_rows(); + item_subs->set_cond_guard_var(0, TRUE); + } + else + { + uint i; + uint ncols= cache->cols(); + /* + Turn off the predicates that are based on column compares for + which the left part is currently NULL + */ + for (i= 0; i < ncols; i++) + { + if (cache->element_index(i)->null_value) + item_subs->set_cond_guard_var(i, FALSE); + } + + (void) args[1]->val_bool_result(); + result_for_null_param= null_value= !item_subs->engine->no_rows(); + + /* Turn all predicates back on */ + for (i= 0; i < ncols; i++) + item_subs->set_cond_guard_var(i, TRUE); + } + } + } return 0; } - bool tmp= args[1]->val_bool_result(); + tmp= args[1]->val_bool_result(); null_value= args[1]->null_value; return tmp; } @@ -944,15 +1129,17 @@ longlong Item_func_strcmp::val_int() void Item_func_interval::fix_length_and_dec() { - use_decimal_comparison= (row->el(0)->result_type() == DECIMAL_RESULT) || - (row->el(0)->result_type() == INT_RESULT); + use_decimal_comparison= ((row->element_index(0)->result_type() == + DECIMAL_RESULT) || + (row->element_index(0)->result_type() == + INT_RESULT)); if (row->cols() > 8) { bool consts=1; for (uint i=1 ; consts && i < row->cols() ; i++) { - consts&= row->el(i)->const_item(); + consts&= row->element_index(i)->const_item(); } if (consts && @@ -963,7 +1150,7 @@ void Item_func_interval::fix_length_and_dec() { for (uint i=1 ; i < row->cols(); i++) { - Item *el= row->el(i); + Item *el= row->element_index(i); interval_range *range= intervals + (i-1); if ((el->result_type() == DECIMAL_RESULT) || (el->result_type() == INT_RESULT)) @@ -988,7 +1175,7 @@ void Item_func_interval::fix_length_and_dec() { for (uint i=1 ; i < row->cols(); i++) { - intervals[i-1].dbl= row->el(i)->val_real(); + intervals[i-1].dbl= row->element_index(i)->val_real(); } } } @@ -1028,15 +1215,15 @@ longlong Item_func_interval::val_int() if (use_decimal_comparison) { - dec= row->el(0)->val_decimal(&dec_buf); - if (row->el(0)->null_value) + dec= row->element_index(0)->val_decimal(&dec_buf); + if (row->element_index(0)->null_value) return -1; my_decimal2double(E_DEC_FATAL_ERROR, dec, &value); } else { - value= row->el(0)->val_real(); - if (row->el(0)->null_value) + value= row->element_index(0)->val_real(); + if (row->element_index(0)->null_value) return -1; } @@ -1072,16 +1259,16 @@ longlong Item_func_interval::val_int() for (i=1 ; i < row->cols() ; i++) { - Item *el= row->el(i); + Item *el= row->element_index(i); if (use_decimal_comparison && ((el->result_type() == DECIMAL_RESULT) || (el->result_type() == INT_RESULT))) { - my_decimal e_dec_buf, *e_dec= row->el(i)->val_decimal(&e_dec_buf); + my_decimal e_dec_buf, *e_dec= row->element_index(i)->val_decimal(&e_dec_buf); if (my_decimal_cmp(e_dec, dec) > 0) return i-1; } - else if (row->el(i)->val_real() > value) + else if (row->element_index(i)->val_real() > value) return i-1; } return i-1; @@ -1158,11 +1345,11 @@ void Item_func_between::fix_length_and_dec() They are compared as integers, so for const item this time-consuming conversion can be done only once, not for every single comparison */ - if (args[0]->type() == FIELD_ITEM && + if (args[0]->real_item()->type() == FIELD_ITEM && thd->lex->sql_command != SQLCOM_CREATE_VIEW && thd->lex->sql_command != SQLCOM_SHOW_CREATE) { - Field *field=((Item_field*) args[0])->field; + Field *field=((Item_field*) (args[0]->real_item()))->field; if (field->can_be_compared_as_longlong()) { /* @@ -1445,6 +1632,7 @@ Item_func_if::fix_length_and_dec() { maybe_null=args[1]->maybe_null || args[2]->maybe_null; decimals= max(args[1]->decimals, args[2]->decimals); + unsigned_flag=args[1]->unsigned_flag && args[2]->unsigned_flag; enum Item_result arg1_type=args[1]->result_type(); enum Item_result arg2_type=args[2]->result_type(); @@ -1474,12 +1662,20 @@ Item_func_if::fix_length_and_dec() collation.set(&my_charset_bin); // Number } } - max_length= - (cached_result_type == DECIMAL_RESULT || cached_result_type == INT_RESULT) ? - (max(args[1]->max_length - args[1]->decimals, - args[2]->max_length - args[2]->decimals) + decimals + - (unsigned_flag ? 0 : 1) ) : - max(args[1]->max_length, args[2]->max_length); + + if ((cached_result_type == DECIMAL_RESULT ) + || (cached_result_type == INT_RESULT)) + { + int len1= args[1]->max_length - args[1]->decimals + - (args[1]->unsigned_flag ? 0 : 1); + + int len2= args[2]->max_length - args[2]->decimals + - (args[2]->unsigned_flag ? 0 : 1); + + max_length=max(len1, len2) + decimals + (unsigned_flag ? 0 : 1); + } + else + max_length= max(args[1]->max_length, args[2]->max_length); } @@ -1775,7 +1971,9 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref) buff should match stack usage from Item_func_case::val_int() -> Item_func_case::find_item() */ +#ifndef EMBEDDED_LIBRARY char buff[MAX_FIELD_WIDTH*2+sizeof(String)*2+sizeof(String*)*2+sizeof(double)*2+sizeof(longlong)*2]; +#endif bool res= Item_func::fix_fields(thd, ref); /* Call check_stack_overrun after fix_fields to be sure that stack variable @@ -1792,7 +1990,6 @@ void Item_func_case::fix_length_and_dec() { Item **agg; uint nagg; - THD *thd= current_thd; uint found_types= 0; if (!(agg= (Item**) sql_alloc(sizeof(Item*)*(ncases+1)))) return; @@ -1993,9 +2190,100 @@ void Item_func_coalesce::fix_length_and_dec() Classes and function for the IN operator ****************************************************************************/ -static int cmp_longlong(void *cmp_arg, longlong *a,longlong *b) +/* + Determine which of the signed longlong arguments is bigger + + SYNOPSIS + cmp_longs() + a_val left argument + b_val right argument + + DESCRIPTION + This function will compare two signed longlong arguments + and will return -1, 0, or 1 if left argument is smaller than, + equal to or greater than the right argument. + + RETURN VALUE + -1 left argument is smaller than the right argument. + 0 left argument is equal to the right argument. + 1 left argument is greater than the right argument. +*/ +static inline int cmp_longs (longlong a_val, longlong b_val) { - return *a < *b ? -1 : *a == *b ? 0 : 1; + return a_val < b_val ? -1 : a_val == b_val ? 0 : 1; +} + + +/* + Determine which of the unsigned longlong arguments is bigger + + SYNOPSIS + cmp_ulongs() + a_val left argument + b_val right argument + + DESCRIPTION + This function will compare two unsigned longlong arguments + and will return -1, 0, or 1 if left argument is smaller than, + equal to or greater than the right argument. + + RETURN VALUE + -1 left argument is smaller than the right argument. + 0 left argument is equal to the right argument. + 1 left argument is greater than the right argument. +*/ +static inline int cmp_ulongs (ulonglong a_val, ulonglong b_val) +{ + return a_val < b_val ? -1 : a_val == b_val ? 0 : 1; +} + + +/* + Compare two integers in IN value list format (packed_longlong) + + SYNOPSIS + cmp_longlong() + cmp_arg an argument passed to the calling function (qsort2) + a left argument + b right argument + + DESCRIPTION + This function will compare two integer arguments in the IN value list + format and will return -1, 0, or 1 if left argument is smaller than, + equal to or greater than the right argument. + It's used in sorting the IN values list and finding an element in it. + Depending on the signedness of the arguments cmp_longlong() will + compare them as either signed (using cmp_longs()) or unsigned (using + cmp_ulongs()). + + RETURN VALUE + -1 left argument is smaller than the right argument. + 0 left argument is equal to the right argument. + 1 left argument is greater than the right argument. +*/ +int cmp_longlong(void *cmp_arg, + in_longlong::packed_longlong *a, + in_longlong::packed_longlong *b) +{ + if (a->unsigned_flag != b->unsigned_flag) + { + /* + One of the args is unsigned and is too big to fit into the + positive signed range. Report no match. + */ + if (a->unsigned_flag && ((ulonglong) a->val) > LONGLONG_MAX || + b->unsigned_flag && ((ulonglong) b->val) > LONGLONG_MAX) + return a->unsigned_flag ? 1 : -1; + /* + Although the signedness differs both args can fit into the signed + positive range. Make them signed and compare as usual. + */ + return cmp_longs (a->val, b->val); + } + if (a->unsigned_flag) + return cmp_ulongs ((ulonglong) a->val, (ulonglong) b->val); + else + return cmp_longs (a->val, b->val); } static int cmp_double(void *cmp_arg, double *a,double *b) @@ -2120,19 +2408,23 @@ void in_row::set(uint pos, Item *item) } in_longlong::in_longlong(uint elements) - :in_vector(elements,sizeof(longlong),(qsort2_cmp) cmp_longlong, 0) + :in_vector(elements,sizeof(packed_longlong),(qsort2_cmp) cmp_longlong, 0) {} void in_longlong::set(uint pos,Item *item) { - ((longlong*) base)[pos]=item->val_int(); + struct packed_longlong *buff= &((packed_longlong*) base)[pos]; + + buff->val= item->val_int(); + buff->unsigned_flag= item->unsigned_flag; } byte *in_longlong::get_value(Item *item) { - tmp= item->val_int(); + tmp.val= item->val_int(); if (item->null_value) return 0; + tmp.unsigned_flag= item->unsigned_flag; return (byte*) &tmp; } @@ -2226,7 +2518,7 @@ cmp_item* cmp_item_row::make_same() cmp_item_row::~cmp_item_row() { DBUG_ENTER("~cmp_item_row"); - DBUG_PRINT("enter",("this: 0x%lx", this)); + DBUG_PRINT("enter",("this: 0x%lx", (long) this)); if (comparators) { for (uint i= 0; i < n; i++) @@ -2253,11 +2545,11 @@ void cmp_item_row::store_value(Item *item) { if (!comparators[i]) if (!(comparators[i]= - cmp_item::get_comparator(item->el(i)->result_type(), - item->el(i)->collation.collation))) + cmp_item::get_comparator(item->element_index(i)->result_type(), + item->element_index(i)->collation.collation))) break; // new failed - comparators[i]->store_value(item->el(i)); - item->null_value|= item->el(i)->null_value; + comparators[i]->store_value(item->element_index(i)); + item->null_value|= item->element_index(i)->null_value; } } DBUG_VOID_RETURN; @@ -2282,8 +2574,8 @@ void cmp_item_row::store_value_by_template(cmp_item *t, Item *item) if (!(comparators[i]= tmpl->comparators[i]->make_same())) break; // new failed comparators[i]->store_value_by_template(tmpl->comparators[i], - item->el(i)); - item->null_value|= item->el(i)->null_value; + item->element_index(i)); + item->null_value|= item->element_index(i)->null_value; } } } @@ -2301,9 +2593,9 @@ int cmp_item_row::cmp(Item *arg) arg->bring_value(); for (uint i=0; i < n; i++) { - if (comparators[i]->cmp(arg->el(i))) + if (comparators[i]->cmp(arg->element_index(i))) { - if (!arg->el(i)->null_value) + if (!arg->element_index(i)->null_value) return 1; was_null= 1; } @@ -2314,11 +2606,11 @@ int cmp_item_row::cmp(Item *arg) int cmp_item_row::compare(cmp_item *c) { - cmp_item_row *cmp= (cmp_item_row *) c; + cmp_item_row *l_cmp= (cmp_item_row *) c; for (uint i=0; i < n; i++) { int res; - if ((res= comparators[i]->compare(cmp->comparators[i]))) + if ((res= comparators[i]->compare(l_cmp->comparators[i]))) return res; } return 0; @@ -2345,8 +2637,8 @@ int cmp_item_decimal::cmp(Item *arg) int cmp_item_decimal::compare(cmp_item *arg) { - cmp_item_decimal *cmp= (cmp_item_decimal*) arg; - return my_decimal_cmp(&value, &cmp->value); + cmp_item_decimal *l_cmp= (cmp_item_decimal*) arg; + return my_decimal_cmp(&value, &l_cmp->value); } @@ -2433,6 +2725,7 @@ void Item_func_in::fix_length_and_dec() THD *thd= current_thd; uint found_types= 0; uint type_cnt= 0, i; + Item_result cmp_type= STRING_RESULT; left_result_type= args[0]->result_type(); found_types= collect_cmp_types(args, arg_count); @@ -2447,25 +2740,53 @@ void Item_func_in::fix_length_and_dec() for (i= 0; i <= (uint)DECIMAL_RESULT; i++) { if (found_types & 1 << i) + { (type_cnt)++; + cmp_type= (Item_result) i; + } } + + if (type_cnt == 1) + { + if (cmp_type == STRING_RESULT && + agg_arg_charsets(cmp_collation, args, arg_count, MY_COLL_CMP_CONV, 1)) + return; + arg_types_compatible= TRUE; + } + /* Row item with NULLs inside can return NULL or FALSE => they can't be processed as static */ if (type_cnt == 1 && const_itm && !nulls_in_row()) { - uint tmp_type; - Item_result cmp_type; - /* Only one cmp type was found. Extract it here */ - for (tmp_type= 0; found_types - 1; found_types>>= 1) - tmp_type++; - cmp_type= (Item_result)tmp_type; - + /* + IN must compare INT/DATE/DATETIME/TIMESTAMP columns and constants + as int values (the same way as equality does). + So we must check here if the column on the left and all the constant + values on the right can be compared as integers and adjust the + comparison type accordingly. + */ + if (args[0]->real_item()->type() == FIELD_ITEM && + thd->lex->sql_command != SQLCOM_CREATE_VIEW && + thd->lex->sql_command != SQLCOM_SHOW_CREATE && + cmp_type != INT_RESULT) + { + Field *field= ((Item_field*) (args[0]->real_item()))->field; + if (field->can_be_compared_as_longlong()) + { + bool all_converted= TRUE; + for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++) + { + if (!convert_constant_item (thd, field, &arg[0])) + all_converted= FALSE; + } + if (all_converted) + cmp_type= INT_RESULT; + } + } switch (cmp_type) { case STRING_RESULT: - if (agg_arg_charsets(cmp_collation, args, arg_count, MY_COLL_CMP_CONV, 1)) - return; array=new in_string(arg_count - 1,(qsort2_cmp) srtcmp_in, cmp_collation.collation); break; @@ -2516,7 +2837,6 @@ void Item_func_in::fix_length_and_dec() } } } - maybe_null= args[0]->maybe_null; max_length= 1; } @@ -3046,7 +3366,7 @@ longlong Item_func_isnull::val_int() Handle optimization if the argument can't be null This has to be here because of the test in update_used_tables(). */ - if (!used_tables_cache) + if (!used_tables_cache && !with_subselect) return cached_value; return args[0]->is_null() ? 1: 0; } @@ -3055,10 +3375,10 @@ longlong Item_is_not_null_test::val_int() { DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_is_not_null_test::val_int"); - if (!used_tables_cache) + if (!used_tables_cache && !with_subselect) { owner->was_null|= (!cached_value); - DBUG_PRINT("info", ("cached :%d", cached_value)); + DBUG_PRINT("info", ("cached: %ld", (long) cached_value)); DBUG_RETURN(cached_value); } if (args[0]->is_null()) @@ -3082,7 +3402,7 @@ void Item_is_not_null_test::update_used_tables() else { args[0]->update_used_tables(); - if (!(used_tables_cache=args[0]->used_tables())) + if (!(used_tables_cache=args[0]->used_tables()) && !with_subselect) { /* Remember if the value is always NULL or never NULL */ cached_value= (longlong) !args[0]->is_null(); @@ -4051,11 +4371,9 @@ longlong Item_equal::val_int() void Item_equal::fix_length_and_dec() { - Item *item= const_item ? const_item : get_first(); + Item *item= get_first(); eval_item= cmp_item::get_comparator(item->result_type(), item->collation.collation); - if (item->result_type() == STRING_RESULT) - eval_item->cmp_charset= cmp_collation.collation; } bool Item_equal::walk(Item_processor processor, bool walk_subquery, byte *arg) diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index c2d93fdd5ef..edc905d50ff 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -35,6 +34,7 @@ class Arg_comparator: public Sql_alloc arg_cmp_func func; Item_bool_func2 *owner; Arg_comparator *comparators; // used only for compare_row() + double precision; public: DTCollation cmp_collation; @@ -81,6 +81,8 @@ public: int compare_e_int(); // compare args[0] & args[1] int compare_e_int_diff_signedness(); int compare_e_row(); // compare args[0] & args[1] + int compare_real_fixed(); + int compare_e_real_fixed(); static arg_cmp_func comparator_matrix [5][2]; @@ -99,26 +101,131 @@ public: uint decimal_precision() const { return 1; } }; + +/** + Abstract Item class, to represent <code>X IS [NOT] (TRUE | FALSE)</code> + boolean predicates. +*/ + +class Item_func_truth : public Item_bool_func +{ +public: + virtual bool val_bool(); + virtual longlong val_int(); + virtual void fix_length_and_dec(); + virtual void print(String *str); + +protected: + Item_func_truth(Item *a, bool a_value, bool a_affirmative) + : Item_bool_func(a), value(a_value), affirmative(a_affirmative) + {} + + ~Item_func_truth() + {} +private: + /** + True for <code>X IS [NOT] TRUE</code>, + false for <code>X IS [NOT] FALSE</code> predicates. + */ + const bool value; + /** + True for <code>X IS Y</code>, false for <code>X IS NOT Y</code> predicates. + */ + const bool affirmative; +}; + + +/** + This Item represents a <code>X IS TRUE</code> boolean predicate. +*/ + +class Item_func_istrue : public Item_func_truth +{ +public: + Item_func_istrue(Item *a) : Item_func_truth(a, true, true) {} + ~Item_func_istrue() {} + virtual const char* func_name() const { return "istrue"; } +}; + + +/** + This Item represents a <code>X IS NOT TRUE</code> boolean predicate. +*/ + +class Item_func_isnottrue : public Item_func_truth +{ +public: + Item_func_isnottrue(Item *a) : Item_func_truth(a, true, false) {} + ~Item_func_isnottrue() {} + virtual const char* func_name() const { return "isnottrue"; } +}; + + +/** + This Item represents a <code>X IS FALSE</code> boolean predicate. +*/ + +class Item_func_isfalse : public Item_func_truth +{ +public: + Item_func_isfalse(Item *a) : Item_func_truth(a, false, true) {} + ~Item_func_isfalse() {} + virtual const char* func_name() const { return "isfalse"; } +}; + + +/** + This Item represents a <code>X IS NOT FALSE</code> boolean predicate. +*/ + +class Item_func_isnotfalse : public Item_func_truth +{ +public: + Item_func_isnotfalse(Item *a) : Item_func_truth(a, false, false) {} + ~Item_func_isnotfalse() {} + virtual const char* func_name() const { return "isnotfalse"; } +}; + + class Item_cache; +#define UNKNOWN ((my_bool)-1) + + +/* + Item_in_optimizer(left_expr, Item_in_subselect(...)) + + Item_in_optimizer is used to wrap an instance of Item_in_subselect. This + class does the following: + - Evaluate the left expression and store it in Item_cache_* object (to + avoid re-evaluating it many times during subquery execution) + - Shortcut the evaluation of "NULL IN (...)" to NULL in the cases where we + don't care if the result is NULL or FALSE. + + NOTE + It is not quite clear why the above listed functionality should be + placed into a separate class called 'Item_in_optimizer'. +*/ + class Item_in_optimizer: public Item_bool_func { protected: Item_cache *cache; bool save_cache; + /* + Stores the value of "NULL IN (SELECT ...)" for uncorrelated subqueries: + UNKNOWN - "NULL in (SELECT ...)" has not yet been evaluated + FALSE - result is FALSE + TRUE - result is NULL + */ + my_bool result_for_null_param; public: Item_in_optimizer(Item *a, Item_in_subselect *b): - Item_bool_func(a, my_reinterpret_cast(Item *)(b)), cache(0), save_cache(0) + Item_bool_func(a, my_reinterpret_cast(Item *)(b)), cache(0), + save_cache(0), result_for_null_param(UNKNOWN) {} bool fix_fields(THD *, Item **); bool fix_left(THD *thd, Item **ref); bool is_null(); - /* - Item_in_optimizer item is special boolean function. On value request - (one of val, val_int or val_str methods) it evaluate left expression - of IN by storing it value in cache item (one of Item_cache* items), - then it test cache is it NULL. If left expression (cache) is NULL then - Item_in_optimizer return NULL, else it evaluate Item_in_subselect. - */ longlong val_int(); void cleanup(); const char *func_name() const { return "<in_optimizer>"; } @@ -253,14 +360,17 @@ public: const char *func_name() const { return "not"; } Item *neg_transformer(THD *thd); bool check_partition_func_processor(byte *int_arg) {return FALSE;} + void print(String *str); }; class Item_maxmin_subselect; /* + trigcond<param>(arg) ::= param? arg : TRUE + The class Item_func_trig_cond is used for guarded predicates which are employed only for internal purposes. - A guarded predicates is an object consisting of an a regular or + A guarded predicate is an object consisting of an a regular or a guarded predicate P and a pointer to a boolean guard variable g. A guarded predicate P/g is evaluated to true if the value of the guard g is false, otherwise it is evaluated to the same value that @@ -278,6 +388,10 @@ class Item_maxmin_subselect; Objects of this class are built only for query execution after the execution plan has been already selected. That's why this class needs only val_int out of generic methods. + + Current uses of Item_func_trig_cond objects: + - To wrap selection conditions when executing outer joins + - To wrap condition that is pushed down into subquery */ class Item_func_trig_cond: public Item_bool_func @@ -289,6 +403,7 @@ public: enum Functype functype() const { return TRIG_COND_FUNC; }; const char *func_name() const { return "trigcond"; }; bool const_item() const { return FALSE; } + bool *get_trig_var() { return trig_var; } }; class Item_func_not_all :public Item_func_not @@ -670,7 +785,16 @@ public: class in_longlong :public in_vector { - longlong tmp; + /* + Here we declare a temporary variable (tmp) of the same type as the + elements of this vector. tmp is used in finding if a given value is in + the list. + */ + struct packed_longlong + { + longlong val; + longlong unsigned_flag; // Use longlong, not bool, to preserve alignment + } tmp; public: in_longlong(uint elements); void set(uint pos,Item *item); @@ -686,9 +810,13 @@ public: } void value_to_item(uint pos, Item *item) { - ((Item_int*)item)->value= ((longlong*)base)[pos]; + ((Item_int*) item)->value= ((packed_longlong*) base)[pos].val; + ((Item_int*) item)->unsigned_flag= (my_bool) + ((packed_longlong*) base)[pos].unsigned_flag; } Item_result result_type() { return INT_RESULT; } + + friend int cmp_longlong(void *cmp_arg, packed_longlong *a,packed_longlong *b); }; class in_double :public in_vector @@ -789,10 +917,10 @@ public: return (value_res ? (res ? sortcmp(value_res, res, cmp_charset) : 1) : (res ? -1 : 0)); } - int compare(cmp_item *c) + int compare(cmp_item *ci) { - cmp_item_string *cmp= (cmp_item_string *)c; - return sortcmp(value_res, cmp->value_res, cmp_charset); + cmp_item_string *l_cmp= (cmp_item_string *) ci; + return sortcmp(value_res, l_cmp->value_res, cmp_charset); } cmp_item *make_same(); void set_charset(CHARSET_INFO *cs) @@ -815,10 +943,10 @@ public: { return value != arg->val_int(); } - int compare(cmp_item *c) + int compare(cmp_item *ci) { - cmp_item_int *cmp= (cmp_item_int *)c; - return (value < cmp->value) ? -1 : ((value == cmp->value) ? 0 : 1); + cmp_item_int *l_cmp= (cmp_item_int *)ci; + return (value < l_cmp->value) ? -1 : ((value == l_cmp->value) ? 0 : 1); } cmp_item *make_same(); }; @@ -836,10 +964,10 @@ public: { return value != arg->val_real(); } - int compare(cmp_item *c) + int compare(cmp_item *ci) { - cmp_item_real *cmp= (cmp_item_real *)c; - return (value < cmp->value)? -1 : ((value == cmp->value) ? 0 : 1); + cmp_item_real *l_cmp= (cmp_item_real *) ci; + return (value < l_cmp->value)? -1 : ((value == l_cmp->value) ? 0 : 1); } cmp_item *make_same(); }; @@ -905,10 +1033,10 @@ public: DBUG_ASSERT(0); return 1; } - int compare(cmp_item *c) + int compare(cmp_item *ci) { - cmp_item_string *cmp= (cmp_item_string *)c; - return sortcmp(value_res, cmp->value_res, cmp_charset); + cmp_item_string *l_cmp= (cmp_item_string *) ci; + return sortcmp(value_res, l_cmp->value_res, cmp_charset); } cmp_item *make_same() { @@ -1009,14 +1137,25 @@ public: class Item_func_in :public Item_func_opt_neg { public: + Item_result cmp_type; + /* + an array of values when the right hand arguments of IN + are all SQL constant and there are no nulls + */ in_vector *array; bool have_null; + /* + true when all arguments of the IN clause are of compatible types + and can be used safely as comparisons for key conditions + */ + bool arg_types_compatible; Item_result left_result_type; cmp_item *cmp_items[5]; /* One cmp_item for each result type */ DTCollation cmp_collation; Item_func_in(List<Item> &list) - :Item_func_opt_neg(list), array(0), have_null(0) + :Item_func_opt_neg(list), array(0), have_null(0), + arg_types_compatible(FALSE) { bzero(&cmp_items, sizeof(cmp_items)); allowed_arg_cols= 0; // Fetch this value from first argument @@ -1040,7 +1179,7 @@ public: DBUG_VOID_RETURN; } optimize_type select_optimize() const - { return array ? OPTIMIZE_KEY : OPTIMIZE_NONE; } + { return OPTIMIZE_KEY; } void print(String *str); enum Functype functype() const { return IN_FUNC; } const char *func_name() const { return " IN "; } @@ -1078,7 +1217,8 @@ public: else { args[0]->update_used_tables(); - if ((const_item_cache= !(used_tables_cache= args[0]->used_tables()))) + if ((const_item_cache= !(used_tables_cache= args[0]->used_tables())) && + !with_subselect) { /* Remember if the value is always NULL or never NULL */ cached_value= (longlong) args[0]->is_null(); @@ -1095,6 +1235,11 @@ public: /* Functions used by HAVING for rewriting IN subquery */ class Item_in_subselect; + +/* + This is like IS NOT NULL but it also remembers if it ever has + encountered a NULL. +*/ class Item_is_not_null_test :public Item_func_isnull { Item_in_subselect* owner; @@ -1341,7 +1486,6 @@ class Item_equal: public Item_bool_func Item *const_item; /* optional constant item equal to fields items */ cmp_item *eval_item; bool cond_false; - DTCollation cmp_collation; public: inline Item_equal() : Item_bool_func(), const_item(0), eval_item(0), cond_false(0) @@ -1415,7 +1559,7 @@ public: Item_cond_and() :Item_cond() {} Item_cond_and(Item *i1,Item *i2) :Item_cond(i1,i2) {} Item_cond_and(THD *thd, Item_cond_and *item) :Item_cond(thd, item) {} - Item_cond_and(List<Item> &list): Item_cond(list) {} + Item_cond_and(List<Item> &list_arg): Item_cond(list_arg) {} enum Functype functype() const { return COND_AND_FUNC; } longlong val_int(); const char *func_name() const { return "and"; } @@ -1437,7 +1581,7 @@ public: Item_cond_or() :Item_cond() {} Item_cond_or(Item *i1,Item *i2) :Item_cond(i1,i2) {} Item_cond_or(THD *thd, Item_cond_or *item) :Item_cond(thd, item) {} - Item_cond_or(List<Item> &list): Item_cond(list) {} + Item_cond_or(List<Item> &list_arg): Item_cond(list_arg) {} enum Functype functype() const { return COND_OR_FUNC; } longlong val_int(); const char *func_name() const { return "or"; } diff --git a/sql/item_create.cc b/sql/item_create.cc index 3a93ec6e516..ff5825ef389 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -14,728 +13,5010 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* Functions to create an item. Used by lex.h */ +/* Functions to create an item. Used by sql_yacc.yy */ #include "mysql_priv.h" +#include "item_create.h" +#include "sp_head.h" +#include "sp.h" + +/* +============================================================================= + LOCAL DECLARATIONS +============================================================================= +*/ + +/** + Adapter for native functions with a variable number of arguments. + The main use of this class is to discard the following calls: + <code>foo(expr1 AS name1, expr2 AS name2, ...)</code> + which are syntactically correct (the syntax can refer to a UDF), + but semantically invalid for native functions. +*/ + +class Create_native_func : public Create_func +{ +public: + virtual Item* create(THD *thd, LEX_STRING name, List<Item> *item_list); + + /** + Builder method, with no arguments. + @param thd The current thread + @param name The native function name + @param item_list The function parameters, none of which are named + @return An item representing the function call + */ + virtual Item* create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) = 0; + +protected: + /** Constructor. */ + Create_native_func() {} + /** Destructor. */ + virtual ~Create_native_func() {} +}; + + +/** + Adapter for functions that takes exactly zero arguments. +*/ + +class Create_func_arg0 : public Create_func +{ +public: + virtual Item* create(THD *thd, LEX_STRING name, List<Item> *item_list); + + /** + Builder method, with no arguments. + @param thd The current thread + @return An item representing the function call + */ + virtual Item* create(THD *thd) = 0; + +protected: + /** Constructor. */ + Create_func_arg0() {} + /** Destructor. */ + virtual ~Create_func_arg0() {} +}; + + +/** + Adapter for functions that takes exactly one argument. +*/ + +class Create_func_arg1 : public Create_func +{ +public: + virtual Item* create(THD *thd, LEX_STRING name, List<Item> *item_list); + + /** + Builder method, with one argument. + @param thd The current thread + @param arg1 The first argument of the function + @return An item representing the function call + */ + virtual Item* create(THD *thd, Item *arg1) = 0; + +protected: + /** Constructor. */ + Create_func_arg1() {} + /** Destructor. */ + virtual ~Create_func_arg1() {} +}; + + +/** + Adapter for functions that takes exactly two arguments. +*/ + +class Create_func_arg2 : public Create_func +{ +public: + virtual Item* create(THD *thd, LEX_STRING name, List<Item> *item_list); + + /** + Builder method, with two arguments. + @param thd The current thread + @param arg1 The first argument of the function + @param arg2 The second argument of the function + @return An item representing the function call + */ + virtual Item* create(THD *thd, Item *arg1, Item *arg2) = 0; + +protected: + /** Constructor. */ + Create_func_arg2() {} + /** Destructor. */ + virtual ~Create_func_arg2() {} +}; + + +/** + Adapter for functions that takes exactly three arguments. +*/ + +class Create_func_arg3 : public Create_func +{ +public: + virtual Item* create(THD *thd, LEX_STRING name, List<Item> *item_list); + + /** + Builder method, with three arguments. + @param thd The current thread + @param arg1 The first argument of the function + @param arg2 The second argument of the function + @param arg3 The third argument of the function + @return An item representing the function call + */ + virtual Item* create(THD *thd, Item *arg1, Item *arg2, Item *arg3) = 0; + +protected: + /** Constructor. */ + Create_func_arg3() {} + /** Destructor. */ + virtual ~Create_func_arg3() {} +}; + + +/** + Function builder for Stored Functions. +*/ + +class Create_sp_func : public Create_qfunc +{ +public: + virtual Item* create(THD *thd, LEX_STRING db, LEX_STRING name, + List<Item> *item_list); + + static Create_sp_func s_singleton; + +protected: + /** Constructor. */ + Create_sp_func() {} + /** Destructor. */ + virtual ~Create_sp_func() {} +}; -Item *create_func_abs(Item* a) + +#ifndef HAVE_SPATIAL +/** + Common (non) builder for geometry functions. + This builder is used in <code>--without-geometry</code> builds only, + to report an error. +*/ + +class Create_func_no_geom : public Create_func +{ +public: + virtual Item* create(THD *thd, LEX_STRING name, List<Item> *item_list); + + /** Singleton. */ + static Create_func_no_geom s_singleton; + +protected: + /** Constructor. */ + Create_func_no_geom() {} + /** Destructor. */ + virtual ~Create_func_no_geom() {} +}; +#endif + + +/* + Concrete functions builders (native functions). + Please keep this list sorted in alphabetical order, + it helps to compare code between versions, and helps with merges conflicts. +*/ + +class Create_func_abs : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_abs s_singleton; + +protected: + Create_func_abs() {} + virtual ~Create_func_abs() {} +}; + + +class Create_func_acos : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_acos s_singleton; + +protected: + Create_func_acos() {} + virtual ~Create_func_acos() {} +}; + + +class Create_func_addtime : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_addtime s_singleton; + +protected: + Create_func_addtime() {} + virtual ~Create_func_addtime() {} +}; + + +class Create_func_aes_encrypt : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_aes_encrypt s_singleton; + +protected: + Create_func_aes_encrypt() {} + virtual ~Create_func_aes_encrypt() {} +}; + + +class Create_func_aes_decrypt : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_aes_decrypt s_singleton; + +protected: + Create_func_aes_decrypt() {} + virtual ~Create_func_aes_decrypt() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_area : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_area s_singleton; + +protected: + Create_func_area() {} + virtual ~Create_func_area() {} +}; +#endif + + +#ifdef HAVE_SPATIAL +class Create_func_as_wkb : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_as_wkb s_singleton; + +protected: + Create_func_as_wkb() {} + virtual ~Create_func_as_wkb() {} +}; +#endif + + +#ifdef HAVE_SPATIAL +class Create_func_as_wkt : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_as_wkt s_singleton; + +protected: + Create_func_as_wkt() {} + virtual ~Create_func_as_wkt() {} +}; +#endif + + +class Create_func_asin : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_asin s_singleton; + +protected: + Create_func_asin() {} + virtual ~Create_func_asin() {} +}; + + +class Create_func_atan : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_atan s_singleton; + +protected: + Create_func_atan() {} + virtual ~Create_func_atan() {} +}; + + +class Create_func_benchmark : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_benchmark s_singleton; + +protected: + Create_func_benchmark() {} + virtual ~Create_func_benchmark() {} +}; + + +class Create_func_bin : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_bin s_singleton; + +protected: + Create_func_bin() {} + virtual ~Create_func_bin() {} +}; + + +class Create_func_bit_count : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_bit_count s_singleton; + +protected: + Create_func_bit_count() {} + virtual ~Create_func_bit_count() {} +}; + + +class Create_func_bit_length : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_bit_length s_singleton; + +protected: + Create_func_bit_length() {} + virtual ~Create_func_bit_length() {} +}; + + +class Create_func_ceiling : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg); + + static Create_func_ceiling s_singleton; + +protected: + Create_func_ceiling() {} + virtual ~Create_func_ceiling() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_centroid : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_centroid s_singleton; + +protected: + Create_func_centroid() {} + virtual ~Create_func_centroid() {} +}; +#endif + + +class Create_func_char_length : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_char_length s_singleton; + +protected: + Create_func_char_length() {} + virtual ~Create_func_char_length() {} +}; + + +class Create_func_coercibility : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_coercibility s_singleton; + +protected: + Create_func_coercibility() {} + virtual ~Create_func_coercibility() {} +}; + + +class Create_func_compress : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_compress s_singleton; + +protected: + Create_func_compress() {} + virtual ~Create_func_compress() {} +}; + + +class Create_func_concat : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_concat s_singleton; + +protected: + Create_func_concat() {} + virtual ~Create_func_concat() {} +}; + + +class Create_func_concat_ws : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_concat_ws s_singleton; + +protected: + Create_func_concat_ws() {} + virtual ~Create_func_concat_ws() {} +}; + + +class Create_func_connection_id : public Create_func_arg0 +{ +public: + virtual Item* create(THD *thd); + + static Create_func_connection_id s_singleton; + +protected: + Create_func_connection_id() {} + virtual ~Create_func_connection_id() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_contains : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_contains s_singleton; + +protected: + Create_func_contains() {} + virtual ~Create_func_contains() {} +}; +#endif + + +class Create_func_conv : public Create_func_arg3 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2, Item *arg3); + + static Create_func_conv s_singleton; + +protected: + Create_func_conv() {} + virtual ~Create_func_conv() {} +}; + + +class Create_func_convert_tz : public Create_func_arg3 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2, Item *arg3); + + static Create_func_convert_tz s_singleton; + +protected: + Create_func_convert_tz() {} + virtual ~Create_func_convert_tz() {} +}; + + +class Create_func_cos : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_cos s_singleton; + +protected: + Create_func_cos() {} + virtual ~Create_func_cos() {} +}; + + +class Create_func_cot : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_cot s_singleton; + +protected: + Create_func_cot() {} + virtual ~Create_func_cot() {} +}; + + +class Create_func_crc32 : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_crc32 s_singleton; + +protected: + Create_func_crc32() {} + virtual ~Create_func_crc32() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_crosses : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_crosses s_singleton; + +protected: + Create_func_crosses() {} + virtual ~Create_func_crosses() {} +}; +#endif + + +class Create_func_date_format : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_date_format s_singleton; + +protected: + Create_func_date_format() {} + virtual ~Create_func_date_format() {} +}; + + +class Create_func_datediff : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_datediff s_singleton; + +protected: + Create_func_datediff() {} + virtual ~Create_func_datediff() {} +}; + + +class Create_func_dayname : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_dayname s_singleton; + +protected: + Create_func_dayname() {} + virtual ~Create_func_dayname() {} +}; + + +class Create_func_dayofmonth : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_dayofmonth s_singleton; + +protected: + Create_func_dayofmonth() {} + virtual ~Create_func_dayofmonth() {} +}; + + +class Create_func_dayofweek : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_dayofweek s_singleton; + +protected: + Create_func_dayofweek() {} + virtual ~Create_func_dayofweek() {} +}; + + +class Create_func_dayofyear : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_dayofyear s_singleton; + +protected: + Create_func_dayofyear() {} + virtual ~Create_func_dayofyear() {} +}; + + +class Create_func_decode : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_decode s_singleton; + +protected: + Create_func_decode() {} + virtual ~Create_func_decode() {} +}; + + +class Create_func_degrees : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_degrees s_singleton; + +protected: + Create_func_degrees() {} + virtual ~Create_func_degrees() {} +}; + + +class Create_func_des_decrypt : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_des_decrypt s_singleton; + +protected: + Create_func_des_decrypt() {} + virtual ~Create_func_des_decrypt() {} +}; + + +class Create_func_des_encrypt : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_des_encrypt s_singleton; + +protected: + Create_func_des_encrypt() {} + virtual ~Create_func_des_encrypt() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_dimension : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_dimension s_singleton; + +protected: + Create_func_dimension() {} + virtual ~Create_func_dimension() {} +}; +#endif + + +#ifdef HAVE_SPATIAL +class Create_func_disjoint : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_disjoint s_singleton; + +protected: + Create_func_disjoint() {} + virtual ~Create_func_disjoint() {} +}; +#endif + + +class Create_func_elt : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_elt s_singleton; + +protected: + Create_func_elt() {} + virtual ~Create_func_elt() {} +}; + + +class Create_func_encode : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_encode s_singleton; + +protected: + Create_func_encode() {} + virtual ~Create_func_encode() {} +}; + + +class Create_func_encrypt : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_encrypt s_singleton; + +protected: + Create_func_encrypt() {} + virtual ~Create_func_encrypt() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_endpoint : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_endpoint s_singleton; + +protected: + Create_func_endpoint() {} + virtual ~Create_func_endpoint() {} +}; +#endif + + +#ifdef HAVE_SPATIAL +class Create_func_envelope : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_envelope s_singleton; + +protected: + Create_func_envelope() {} + virtual ~Create_func_envelope() {} +}; +#endif + + +#ifdef HAVE_SPATIAL +class Create_func_equals : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_equals s_singleton; + +protected: + Create_func_equals() {} + virtual ~Create_func_equals() {} +}; +#endif + + +class Create_func_exp : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_exp s_singleton; + +protected: + Create_func_exp() {} + virtual ~Create_func_exp() {} +}; + + +class Create_func_export_set : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_export_set s_singleton; + +protected: + Create_func_export_set() {} + virtual ~Create_func_export_set() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_exteriorring : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_exteriorring s_singleton; + +protected: + Create_func_exteriorring() {} + virtual ~Create_func_exteriorring() {} +}; +#endif + + +class Create_func_field : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_field s_singleton; + +protected: + Create_func_field() {} + virtual ~Create_func_field() {} +}; + + +class Create_func_find_in_set : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_find_in_set s_singleton; + +protected: + Create_func_find_in_set() {} + virtual ~Create_func_find_in_set() {} +}; + + +class Create_func_floor : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_floor s_singleton; + +protected: + Create_func_floor() {} + virtual ~Create_func_floor() {} +}; + + +class Create_func_format : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_format s_singleton; + +protected: + Create_func_format() {} + virtual ~Create_func_format() {} +}; + + +class Create_func_found_rows : public Create_func_arg0 +{ +public: + virtual Item* create(THD *thd); + + static Create_func_found_rows s_singleton; + +protected: + Create_func_found_rows() {} + virtual ~Create_func_found_rows() {} +}; + + +class Create_func_from_days : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_from_days s_singleton; + +protected: + Create_func_from_days() {} + virtual ~Create_func_from_days() {} +}; + + +class Create_func_from_unixtime : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_from_unixtime s_singleton; + +protected: + Create_func_from_unixtime() {} + virtual ~Create_func_from_unixtime() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_geometry_from_text : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_geometry_from_text s_singleton; + +protected: + Create_func_geometry_from_text() {} + virtual ~Create_func_geometry_from_text() {} +}; +#endif + + +#ifdef HAVE_SPATIAL +class Create_func_geometry_from_wkb : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_geometry_from_wkb s_singleton; + +protected: + Create_func_geometry_from_wkb() {} + virtual ~Create_func_geometry_from_wkb() {} +}; +#endif + + +#ifdef HAVE_SPATIAL +class Create_func_geometry_type : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_geometry_type s_singleton; + +protected: + Create_func_geometry_type() {} + virtual ~Create_func_geometry_type() {} +}; +#endif + + +#ifdef HAVE_SPATIAL +class Create_func_geometryn : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_geometryn s_singleton; + +protected: + Create_func_geometryn() {} + virtual ~Create_func_geometryn() {} +}; +#endif + + +class Create_func_get_lock : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_get_lock s_singleton; + +protected: + Create_func_get_lock() {} + virtual ~Create_func_get_lock() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_glength : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_glength s_singleton; + +protected: + Create_func_glength() {} + virtual ~Create_func_glength() {} +}; +#endif + + +class Create_func_greatest : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_greatest s_singleton; + +protected: + Create_func_greatest() {} + virtual ~Create_func_greatest() {} +}; + + +class Create_func_hex : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_hex s_singleton; + +protected: + Create_func_hex() {} + virtual ~Create_func_hex() {} +}; + + +class Create_func_ifnull : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_ifnull s_singleton; + +protected: + Create_func_ifnull() {} + virtual ~Create_func_ifnull() {} +}; + + +class Create_func_inet_ntoa : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_inet_ntoa s_singleton; + +protected: + Create_func_inet_ntoa() {} + virtual ~Create_func_inet_ntoa() {} +}; + + +class Create_func_inet_aton : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_inet_aton s_singleton; + +protected: + Create_func_inet_aton() {} + virtual ~Create_func_inet_aton() {} +}; + + +class Create_func_instr : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_instr s_singleton; + +protected: + Create_func_instr() {} + virtual ~Create_func_instr() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_interiorringn : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_interiorringn s_singleton; + +protected: + Create_func_interiorringn() {} + virtual ~Create_func_interiorringn() {} +}; +#endif + + +#ifdef HAVE_SPATIAL +class Create_func_intersects : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_intersects s_singleton; + +protected: + Create_func_intersects() {} + virtual ~Create_func_intersects() {} +}; +#endif + + +class Create_func_is_free_lock : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_is_free_lock s_singleton; + +protected: + Create_func_is_free_lock() {} + virtual ~Create_func_is_free_lock() {} +}; + + +class Create_func_is_used_lock : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_is_used_lock s_singleton; + +protected: + Create_func_is_used_lock() {} + virtual ~Create_func_is_used_lock() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_isclosed : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_isclosed s_singleton; + +protected: + Create_func_isclosed() {} + virtual ~Create_func_isclosed() {} +}; +#endif + + +#ifdef HAVE_SPATIAL +class Create_func_isempty : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_isempty s_singleton; + +protected: + Create_func_isempty() {} + virtual ~Create_func_isempty() {} +}; +#endif + + +class Create_func_isnull : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_isnull s_singleton; + +protected: + Create_func_isnull() {} + virtual ~Create_func_isnull() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_issimple : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_issimple s_singleton; + +protected: + Create_func_issimple() {} + virtual ~Create_func_issimple() {} +}; +#endif + + +class Create_func_last_day : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_last_day s_singleton; + +protected: + Create_func_last_day() {} + virtual ~Create_func_last_day() {} +}; + + +class Create_func_last_insert_id : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_last_insert_id s_singleton; + +protected: + Create_func_last_insert_id() {} + virtual ~Create_func_last_insert_id() {} +}; + + +class Create_func_lcase : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_lcase s_singleton; + +protected: + Create_func_lcase() {} + virtual ~Create_func_lcase() {} +}; + + +class Create_func_least : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_least s_singleton; + +protected: + Create_func_least() {} + virtual ~Create_func_least() {} +}; + + +class Create_func_length : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_length s_singleton; + +protected: + Create_func_length() {} + virtual ~Create_func_length() {} +}; + + +class Create_func_ln : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_ln s_singleton; + +protected: + Create_func_ln() {} + virtual ~Create_func_ln() {} +}; + + +class Create_func_load_file : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_load_file s_singleton; + +protected: + Create_func_load_file() {} + virtual ~Create_func_load_file() {} +}; + + +class Create_func_locate : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_locate s_singleton; + +protected: + Create_func_locate() {} + virtual ~Create_func_locate() {} +}; + + +class Create_func_log : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_log s_singleton; + +protected: + Create_func_log() {} + virtual ~Create_func_log() {} +}; + + +class Create_func_log10 : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_log10 s_singleton; + +protected: + Create_func_log10() {} + virtual ~Create_func_log10() {} +}; + + +class Create_func_log2 : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_log2 s_singleton; + +protected: + Create_func_log2() {} + virtual ~Create_func_log2() {} +}; + + +class Create_func_lpad : public Create_func_arg3 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2, Item *arg3); + + static Create_func_lpad s_singleton; + +protected: + Create_func_lpad() {} + virtual ~Create_func_lpad() {} +}; + + +class Create_func_ltrim : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_ltrim s_singleton; + +protected: + Create_func_ltrim() {} + virtual ~Create_func_ltrim() {} +}; + + +class Create_func_makedate : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_makedate s_singleton; + +protected: + Create_func_makedate() {} + virtual ~Create_func_makedate() {} +}; + + +class Create_func_maketime : public Create_func_arg3 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2, Item *arg3); + + static Create_func_maketime s_singleton; + +protected: + Create_func_maketime() {} + virtual ~Create_func_maketime() {} +}; + + +class Create_func_make_set : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_make_set s_singleton; + +protected: + Create_func_make_set() {} + virtual ~Create_func_make_set() {} +}; + + +class Create_func_master_pos_wait : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_master_pos_wait s_singleton; + +protected: + Create_func_master_pos_wait() {} + virtual ~Create_func_master_pos_wait() {} +}; + + +class Create_func_md5 : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_md5 s_singleton; + +protected: + Create_func_md5() {} + virtual ~Create_func_md5() {} +}; + + +class Create_func_monthname : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_monthname s_singleton; + +protected: + Create_func_monthname() {} + virtual ~Create_func_monthname() {} +}; + + +class Create_func_name_const : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_name_const s_singleton; + +protected: + Create_func_name_const() {} + virtual ~Create_func_name_const() {} +}; + + +class Create_func_nullif : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_nullif s_singleton; + +protected: + Create_func_nullif() {} + virtual ~Create_func_nullif() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_numgeometries : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_numgeometries s_singleton; + +protected: + Create_func_numgeometries() {} + virtual ~Create_func_numgeometries() {} +}; +#endif + + +#ifdef HAVE_SPATIAL +class Create_func_numinteriorring : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_numinteriorring s_singleton; + +protected: + Create_func_numinteriorring() {} + virtual ~Create_func_numinteriorring() {} +}; +#endif + + +#ifdef HAVE_SPATIAL +class Create_func_numpoints : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_numpoints s_singleton; + +protected: + Create_func_numpoints() {} + virtual ~Create_func_numpoints() {} +}; +#endif + + +class Create_func_oct : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_oct s_singleton; + +protected: + Create_func_oct() {} + virtual ~Create_func_oct() {} +}; + + +class Create_func_ord : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_ord s_singleton; + +protected: + Create_func_ord() {} + virtual ~Create_func_ord() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_overlaps : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_overlaps s_singleton; + +protected: + Create_func_overlaps() {} + virtual ~Create_func_overlaps() {} +}; +#endif + + +class Create_func_period_add : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_period_add s_singleton; + +protected: + Create_func_period_add() {} + virtual ~Create_func_period_add() {} +}; + + +class Create_func_period_diff : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_period_diff s_singleton; + +protected: + Create_func_period_diff() {} + virtual ~Create_func_period_diff() {} +}; + + +class Create_func_pi : public Create_func_arg0 +{ +public: + virtual Item* create(THD *thd); + + static Create_func_pi s_singleton; + +protected: + Create_func_pi() {} + virtual ~Create_func_pi() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_pointn : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_pointn s_singleton; + +protected: + Create_func_pointn() {} + virtual ~Create_func_pointn() {} +}; +#endif + + +class Create_func_pow : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_pow s_singleton; + +protected: + Create_func_pow() {} + virtual ~Create_func_pow() {} +}; + + +class Create_func_quote : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_quote s_singleton; + +protected: + Create_func_quote() {} + virtual ~Create_func_quote() {} +}; + + +class Create_func_radians : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_radians s_singleton; + +protected: + Create_func_radians() {} + virtual ~Create_func_radians() {} +}; + + +class Create_func_rand : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_rand s_singleton; + +protected: + Create_func_rand() {} + virtual ~Create_func_rand() {} +}; + + +class Create_func_release_lock : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_release_lock s_singleton; + +protected: + Create_func_release_lock() {} + virtual ~Create_func_release_lock() {} +}; + + +class Create_func_reverse : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_reverse s_singleton; + +protected: + Create_func_reverse() {} + virtual ~Create_func_reverse() {} +}; + + +class Create_func_round : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_round s_singleton; + +protected: + Create_func_round() {} + virtual ~Create_func_round() {} +}; + + +class Create_func_row_count : public Create_func_arg0 +{ +public: + virtual Item* create(THD *thd); + + static Create_func_row_count s_singleton; + +protected: + Create_func_row_count() {} + virtual ~Create_func_row_count() {} +}; + + +class Create_func_rpad : public Create_func_arg3 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2, Item *arg3); + + static Create_func_rpad s_singleton; + +protected: + Create_func_rpad() {} + virtual ~Create_func_rpad() {} +}; + + +class Create_func_rtrim : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_rtrim s_singleton; + +protected: + Create_func_rtrim() {} + virtual ~Create_func_rtrim() {} +}; + + +class Create_func_sec_to_time : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_sec_to_time s_singleton; + +protected: + Create_func_sec_to_time() {} + virtual ~Create_func_sec_to_time() {} +}; + + +class Create_func_sha : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_sha s_singleton; + +protected: + Create_func_sha() {} + virtual ~Create_func_sha() {} +}; + + +class Create_func_sign : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_sign s_singleton; + +protected: + Create_func_sign() {} + virtual ~Create_func_sign() {} +}; + + +class Create_func_sin : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_sin s_singleton; + +protected: + Create_func_sin() {} + virtual ~Create_func_sin() {} +}; + + +class Create_func_sleep : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_sleep s_singleton; + +protected: + Create_func_sleep() {} + virtual ~Create_func_sleep() {} +}; + + +class Create_func_soundex : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_soundex s_singleton; + +protected: + Create_func_soundex() {} + virtual ~Create_func_soundex() {} +}; + + +class Create_func_space : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_space s_singleton; + +protected: + Create_func_space() {} + virtual ~Create_func_space() {} +}; + + +class Create_func_sqrt : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_sqrt s_singleton; + +protected: + Create_func_sqrt() {} + virtual ~Create_func_sqrt() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_srid : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_srid s_singleton; + +protected: + Create_func_srid() {} + virtual ~Create_func_srid() {} +}; +#endif + + +#ifdef HAVE_SPATIAL +class Create_func_startpoint : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_startpoint s_singleton; + +protected: + Create_func_startpoint() {} + virtual ~Create_func_startpoint() {} +}; +#endif + + +class Create_func_str_to_date : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_str_to_date s_singleton; + +protected: + Create_func_str_to_date() {} + virtual ~Create_func_str_to_date() {} +}; + + +class Create_func_strcmp : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_strcmp s_singleton; + +protected: + Create_func_strcmp() {} + virtual ~Create_func_strcmp() {} +}; + + +class Create_func_substr_index : public Create_func_arg3 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2, Item *arg3); + + static Create_func_substr_index s_singleton; + +protected: + Create_func_substr_index() {} + virtual ~Create_func_substr_index() {} +}; + + +class Create_func_subtime : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_subtime s_singleton; + +protected: + Create_func_subtime() {} + virtual ~Create_func_subtime() {} +}; + + +class Create_func_tan : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_tan s_singleton; + +protected: + Create_func_tan() {} + virtual ~Create_func_tan() {} +}; + + +class Create_func_time_format : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_time_format s_singleton; + +protected: + Create_func_time_format() {} + virtual ~Create_func_time_format() {} +}; + + +class Create_func_time_to_sec : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_time_to_sec s_singleton; + +protected: + Create_func_time_to_sec() {} + virtual ~Create_func_time_to_sec() {} +}; + + +class Create_func_timediff : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_timediff s_singleton; + +protected: + Create_func_timediff() {} + virtual ~Create_func_timediff() {} +}; + + +class Create_func_to_days : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_to_days s_singleton; + +protected: + Create_func_to_days() {} + virtual ~Create_func_to_days() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_touches : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_touches s_singleton; + +protected: + Create_func_touches() {} + virtual ~Create_func_touches() {} +}; +#endif + + +class Create_func_ucase : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_ucase s_singleton; + +protected: + Create_func_ucase() {} + virtual ~Create_func_ucase() {} +}; + + +class Create_func_uncompress : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_uncompress s_singleton; + +protected: + Create_func_uncompress() {} + virtual ~Create_func_uncompress() {} +}; + + +class Create_func_uncompressed_length : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_uncompressed_length s_singleton; + +protected: + Create_func_uncompressed_length() {} + virtual ~Create_func_uncompressed_length() {} +}; + + +class Create_func_unhex : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_unhex s_singleton; + +protected: + Create_func_unhex() {} + virtual ~Create_func_unhex() {} +}; + + +class Create_func_unix_timestamp : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_unix_timestamp s_singleton; + +protected: + Create_func_unix_timestamp() {} + virtual ~Create_func_unix_timestamp() {} +}; + + +class Create_func_uuid : public Create_func_arg0 +{ +public: + virtual Item* create(THD *thd); + + static Create_func_uuid s_singleton; + +protected: + Create_func_uuid() {} + virtual ~Create_func_uuid() {} +}; + + +class Create_func_version : public Create_func_arg0 +{ +public: + virtual Item* create(THD *thd); + + static Create_func_version s_singleton; + +protected: + Create_func_version() {} + virtual ~Create_func_version() {} +}; + + +class Create_func_weekday : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_weekday s_singleton; + +protected: + Create_func_weekday() {} + virtual ~Create_func_weekday() {} +}; + + +class Create_func_weekofyear : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_weekofyear s_singleton; + +protected: + Create_func_weekofyear() {} + virtual ~Create_func_weekofyear() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_within : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_within s_singleton; + +protected: + Create_func_within() {} + virtual ~Create_func_within() {} +}; +#endif + + +#ifdef HAVE_SPATIAL +class Create_func_x : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_x s_singleton; + +protected: + Create_func_x() {} + virtual ~Create_func_x() {} +}; +#endif + + +class Create_func_xml_extractvalue : public Create_func_arg2 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2); + + static Create_func_xml_extractvalue s_singleton; + +protected: + Create_func_xml_extractvalue() {} + virtual ~Create_func_xml_extractvalue() {} +}; + + +class Create_func_xml_update : public Create_func_arg3 +{ +public: + virtual Item* create(THD *thd, Item *arg1, Item *arg2, Item *arg3); + + static Create_func_xml_update s_singleton; + +protected: + Create_func_xml_update() {} + virtual ~Create_func_xml_update() {} +}; + + +#ifdef HAVE_SPATIAL +class Create_func_y : public Create_func_arg1 +{ +public: + virtual Item* create(THD *thd, Item *arg1); + + static Create_func_y s_singleton; + +protected: + Create_func_y() {} + virtual ~Create_func_y() {} +}; +#endif + + +class Create_func_year_week : public Create_native_func +{ +public: + virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_year_week s_singleton; + +protected: + Create_func_year_week() {} + virtual ~Create_func_year_week() {} +}; + + +/* +============================================================================= + IMPLEMENTATION +============================================================================= +*/ + +/** + Checks if there are named parameters in a parameter list. + The syntax to name parameters in a function call is as follow: + <code>foo(expr AS named, expr named, expr AS "named", expr "named")</code> + @param params The parameter list, can be null + @return true if one or more parameter is named +*/ +static bool has_named_parameters(List<Item> *params) +{ + if (params) + { + Item *param; + List_iterator<Item> it(*params); + while ((param= it++)) + { + if (! param->is_autogenerated_name) + return true; + } + } + + return false; +} + +#ifndef HAVE_SPATIAL +Create_func_no_geom Create_func_no_geom::s_singleton; + +Item* +Create_func_no_geom::create(THD * /* unused */, + LEX_STRING /* unused */, + List<Item> * /* unused */) +{ + /* FIXME: error message can't be translated. */ + my_error(ER_FEATURE_DISABLED, MYF(0), + sym_group_geom.name, sym_group_geom.needed_define); + return NULL; +} +#endif + + +Item* +Create_qfunc::create(THD *thd, LEX_STRING name, List<Item> *item_list) +{ + LEX_STRING db; + if (thd->copy_db_to(&db.str, &db.length)) + return NULL; + + return create(thd, db, name, item_list); +} + + +#ifdef HAVE_DLOPEN +Create_udf_func Create_udf_func::s_singleton; + +Item* +Create_udf_func::create(THD *thd, LEX_STRING name, List<Item> *item_list) +{ + udf_func *udf= find_udf(name.str, name.length); + DBUG_ASSERT(udf); + return create(thd, udf, item_list); +} + + +Item* +Create_udf_func::create(THD *thd, udf_func *udf, List<Item> *item_list) +{ + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + thd->lex->binlog_row_based_if_mixed= TRUE; + + DBUG_ASSERT( (udf->type == UDFTYPE_FUNCTION) + || (udf->type == UDFTYPE_AGGREGATE)); + + switch(udf->returns) { + case STRING_RESULT: + { + if (udf->type == UDFTYPE_FUNCTION) + { + if (arg_count) + func= new (thd->mem_root) Item_func_udf_str(udf, *item_list); + else + func= new (thd->mem_root) Item_func_udf_str(udf); + } + else + { + if (arg_count) + func= new (thd->mem_root) Item_sum_udf_str(udf, *item_list); + else + func= new (thd->mem_root) Item_sum_udf_str(udf); + } + break; + } + case REAL_RESULT: + { + if (udf->type == UDFTYPE_FUNCTION) + { + if (arg_count) + func= new (thd->mem_root) Item_func_udf_float(udf, *item_list); + else + func= new (thd->mem_root) Item_func_udf_float(udf); + } + else + { + if (arg_count) + func= new (thd->mem_root) Item_sum_udf_float(udf, *item_list); + else + func= new (thd->mem_root) Item_sum_udf_float(udf); + } + break; + } + case INT_RESULT: + { + if (udf->type == UDFTYPE_FUNCTION) + { + if (arg_count) + func= new (thd->mem_root) Item_func_udf_int(udf, *item_list); + else + func= new (thd->mem_root) Item_func_udf_int(udf); + } + else + { + if (arg_count) + func= new (thd->mem_root) Item_sum_udf_int(udf, *item_list); + else + func= new (thd->mem_root) Item_sum_udf_int(udf); + } + break; + } + case DECIMAL_RESULT: + { + if (udf->type == UDFTYPE_FUNCTION) + { + if (arg_count) + func= new (thd->mem_root) Item_func_udf_decimal(udf, *item_list); + else + func= new (thd->mem_root) Item_func_udf_decimal(udf); + } + else + { + if (arg_count) + func= new (thd->mem_root) Item_sum_udf_decimal(udf, *item_list); + else + func= new (thd->mem_root) Item_sum_udf_decimal(udf); + } + break; + } + default: + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), "UDF return type"); + } + } + return func; +} +#endif + + +Create_sp_func Create_sp_func::s_singleton; + +Item* +Create_sp_func::create(THD *thd, LEX_STRING db, LEX_STRING name, + List<Item> *item_list) +{ + int arg_count= 0; + Item *func= NULL; + LEX *lex= thd->lex; + sp_name *qname; + + if (has_named_parameters(item_list)) + { + /* + The syntax "db.foo(expr AS p1, expr AS p2, ...) is invalid, + and has been rejected during syntactic parsing already, + because a stored function call may not have named parameters. + + The syntax "foo(expr AS p1, expr AS p2, ...)" is correct, + because it can refer to a User Defined Function call. + For a Stored Function however, this has no semantic. + */ + my_error(ER_WRONG_PARAMETERS_TO_STORED_FCT, MYF(0), name.str); + return NULL; + } + + if (item_list != NULL) + arg_count= item_list->elements; + + qname= new (thd->mem_root) sp_name(db, name); + qname->init_qname(thd); + sp_add_used_routine(lex, thd, qname, TYPE_ENUM_FUNCTION); + + if (arg_count > 0) + func= new (thd->mem_root) Item_func_sp(lex->current_context(), qname, + *item_list); + else + func= new (thd->mem_root) Item_func_sp(lex->current_context(), qname); + + lex->safe_to_cache_query= 0; + return func; +} + + +Item* +Create_native_func::create(THD *thd, LEX_STRING name, List<Item> *item_list) +{ + if (has_named_parameters(item_list)) + { + my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name.str); + return NULL; + } + + return create_native(thd, name, item_list); +} + + +Item* +Create_func_arg0::create(THD *thd, LEX_STRING name, List<Item> *item_list) +{ + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + if (arg_count != 0) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + return NULL; + } + + return create(thd); +} + + +Item* +Create_func_arg1::create(THD *thd, LEX_STRING name, List<Item> *item_list) +{ + int arg_count= 0; + + if (item_list) + arg_count= item_list->elements; + + if (arg_count != 1) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + return NULL; + } + + Item *param_1= item_list->pop(); + + if (! param_1->is_autogenerated_name) + { + my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name.str); + return NULL; + } + + return create(thd, param_1); +} + + +Item* +Create_func_arg2::create(THD *thd, LEX_STRING name, List<Item> *item_list) { - return new Item_func_abs(a); + int arg_count= 0; + + if (item_list) + arg_count= item_list->elements; + + if (arg_count != 2) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + return NULL; + } + + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + + if ( (! param_1->is_autogenerated_name) + || (! param_2->is_autogenerated_name)) + { + my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name.str); + return NULL; + } + + return create(thd, param_1, param_2); } -Item *create_func_acos(Item* a) + +Item* +Create_func_arg3::create(THD *thd, LEX_STRING name, List<Item> *item_list) { - return new Item_func_acos(a); + int arg_count= 0; + + if (item_list) + arg_count= item_list->elements; + + if (arg_count != 3) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + return NULL; + } + + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + Item *param_3= item_list->pop(); + + if ( (! param_1->is_autogenerated_name) + || (! param_2->is_autogenerated_name) + || (! param_3->is_autogenerated_name)) + { + my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name.str); + return NULL; + } + + return create(thd, param_1, param_2, param_3); } -Item *create_func_aes_encrypt(Item* a, Item* b) + +Create_func_abs Create_func_abs::s_singleton; + +Item* +Create_func_abs::create(THD *thd, Item *arg1) { - return new Item_func_aes_encrypt(a, b); + return new (thd->mem_root) Item_func_abs(arg1); } -Item *create_func_aes_decrypt(Item* a, Item* b) + +Create_func_acos Create_func_acos::s_singleton; + +Item* +Create_func_acos::create(THD *thd, Item *arg1) { - return new Item_func_aes_decrypt(a, b); + return new (thd->mem_root) Item_func_acos(arg1); } -Item *create_func_ascii(Item* a) + +Create_func_addtime Create_func_addtime::s_singleton; + +Item* +Create_func_addtime::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_ascii(a); + return new (thd->mem_root) Item_func_add_time(arg1, arg2, 0, 0); } -Item *create_func_ord(Item* a) + +Create_func_aes_encrypt Create_func_aes_encrypt::s_singleton; + +Item* +Create_func_aes_encrypt::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_ord(a); + return new (thd->mem_root) Item_func_aes_encrypt(arg1, arg2); } -Item *create_func_asin(Item* a) + +Create_func_aes_decrypt Create_func_aes_decrypt::s_singleton; + +Item* +Create_func_aes_decrypt::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_asin(a); + return new (thd->mem_root) Item_func_aes_decrypt(arg1, arg2); } -Item *create_func_bin(Item* a) + +#ifdef HAVE_SPATIAL +Create_func_area Create_func_area::s_singleton; + +Item* +Create_func_area::create(THD *thd, Item *arg1) { - return new Item_func_conv(a,new Item_int((int32) 10,2), - new Item_int((int32) 2,1)); + return new (thd->mem_root) Item_func_area(arg1); } +#endif + -Item *create_func_bit_count(Item* a) +#ifdef HAVE_SPATIAL +Create_func_as_wkb Create_func_as_wkb::s_singleton; + +Item* +Create_func_as_wkb::create(THD *thd, Item *arg1) { - return new Item_func_bit_count(a); + return new (thd->mem_root) Item_func_as_wkb(arg1); } +#endif -Item *create_func_ceiling(Item* a) + +#ifdef HAVE_SPATIAL +Create_func_as_wkt Create_func_as_wkt::s_singleton; + +Item* +Create_func_as_wkt::create(THD *thd, Item *arg1) { - return new Item_func_ceiling(a); + return new (thd->mem_root) Item_func_as_wkt(arg1); } +#endif + + +Create_func_asin Create_func_asin::s_singleton; -Item *create_func_connection_id(void) +Item* +Create_func_asin::create(THD *thd, Item *arg1) { - current_thd->lex->safe_to_cache_query= 0; - return new Item_func_connection_id(); + return new (thd->mem_root) Item_func_asin(arg1); } -Item *create_func_conv(Item* a, Item *b, Item *c) + +Create_func_atan Create_func_atan::s_singleton; + +Item* +Create_func_atan::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - return new Item_func_conv(a,b,c); + Item* func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 1: + { + Item *param_1= item_list->pop(); + func= new (thd->mem_root) Item_func_atan(param_1); + break; + } + case 2: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + func= new (thd->mem_root) Item_func_atan(param_1, param_2); + break; + } + default: + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + break; + } + } + + return func; } -Item *create_func_cos(Item* a) + +Create_func_benchmark Create_func_benchmark::s_singleton; + +Item* +Create_func_benchmark::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_cos(a); + thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + return new (thd->mem_root) Item_func_benchmark(arg1, arg2); } -Item *create_func_cot(Item* a) + +Create_func_bin Create_func_bin::s_singleton; + +Item* +Create_func_bin::create(THD *thd, Item *arg1) { - return new Item_func_div(new Item_int((char*) "1",1,1), - new Item_func_tan(a)); + Item *i10= new (thd->mem_root) Item_int((int32) 10,2); + Item *i2= new (thd->mem_root) Item_int((int32) 2,1); + return new (thd->mem_root) Item_func_conv(arg1, i10, i2); } -Item *create_func_date_format(Item* a,Item *b) + +Create_func_bit_count Create_func_bit_count::s_singleton; + +Item* +Create_func_bit_count::create(THD *thd, Item *arg1) { - return new Item_func_date_format(a,b,0); + return new (thd->mem_root) Item_func_bit_count(arg1); } -Item *create_func_dayofmonth(Item* a) + +Create_func_bit_length Create_func_bit_length::s_singleton; + +Item* +Create_func_bit_length::create(THD *thd, Item *arg1) { - return new Item_func_dayofmonth(a); + return new (thd->mem_root) Item_func_bit_length(arg1); } -Item *create_func_dayofweek(Item* a) + +Create_func_ceiling Create_func_ceiling::s_singleton; + +Item* +Create_func_ceiling::create(THD *thd, Item *arg1) { - return new Item_func_weekday(a, 1); + return new (thd->mem_root) Item_func_ceiling(arg1); } -Item *create_func_dayofyear(Item* a) + +#ifdef HAVE_SPATIAL +Create_func_centroid Create_func_centroid::s_singleton; + +Item* +Create_func_centroid::create(THD *thd, Item *arg1) { - return new Item_func_dayofyear(a); + return new (thd->mem_root) Item_func_centroid(arg1); } +#endif + + +Create_func_char_length Create_func_char_length::s_singleton; -Item *create_func_dayname(Item* a) +Item* +Create_func_char_length::create(THD *thd, Item *arg1) { - return new Item_func_dayname(a); + return new (thd->mem_root) Item_func_char_length(arg1); } -Item *create_func_degrees(Item *a) + +Create_func_coercibility Create_func_coercibility::s_singleton; + +Item* +Create_func_coercibility::create(THD *thd, Item *arg1) { - return new Item_func_units((char*) "degrees",a,180/M_PI,0.0); + return new (thd->mem_root) Item_func_coercibility(arg1); } -Item *create_func_exp(Item* a) + +Create_func_concat Create_func_concat::s_singleton; + +Item* +Create_func_concat::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - return new Item_func_exp(a); + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + if (arg_count < 1) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + return NULL; + } + + return new (thd->mem_root) Item_func_concat(*item_list); } -Item *create_func_find_in_set(Item* a, Item *b) + +Create_func_concat_ws Create_func_concat_ws::s_singleton; + +Item* +Create_func_concat_ws::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - return new Item_func_find_in_set(a, b); + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + /* "WS" stands for "With Separator": this function takes 2+ arguments */ + if (arg_count < 2) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + return NULL; + } + + return new (thd->mem_root) Item_func_concat_ws(*item_list); } -Item *create_func_floor(Item* a) + +Create_func_compress Create_func_compress::s_singleton; + +Item* +Create_func_compress::create(THD *thd, Item *arg1) { - return new Item_func_floor(a); + return new (thd->mem_root) Item_func_compress(arg1); } -Item *create_func_found_rows(void) + +Create_func_connection_id Create_func_connection_id::s_singleton; + +Item* +Create_func_connection_id::create(THD *thd) { - THD *thd=current_thd; thd->lex->safe_to_cache_query= 0; - return new Item_func_found_rows(); + return new (thd->mem_root) Item_func_connection_id(); } -Item *create_func_from_days(Item* a) + +#ifdef HAVE_SPATIAL +Create_func_contains Create_func_contains::s_singleton; + +Item* +Create_func_contains::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_from_days(a); + return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2, + Item_func::SP_CONTAINS_FUNC); } +#endif + -Item *create_func_get_lock(Item* a, Item *b) +Create_func_conv Create_func_conv::s_singleton; + +Item* +Create_func_conv::create(THD *thd, Item *arg1, Item *arg2, Item *arg3) { - current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - return new Item_func_get_lock(a, b); + return new (thd->mem_root) Item_func_conv(arg1, arg2, arg3); } -Item *create_func_hex(Item *a) + +Create_func_convert_tz Create_func_convert_tz::s_singleton; + +Item* +Create_func_convert_tz::create(THD *thd, Item *arg1, Item *arg2, Item *arg3) { - return new Item_func_hex(a); + if (thd->lex->add_time_zone_tables_to_query_tables(thd)) + return NULL; + + return new (thd->mem_root) Item_func_convert_tz(arg1, arg2, arg3); } -Item *create_func_inet_ntoa(Item* a) + +Create_func_cos Create_func_cos::s_singleton; + +Item* +Create_func_cos::create(THD *thd, Item *arg1) { - return new Item_func_inet_ntoa(a); + return new (thd->mem_root) Item_func_cos(arg1); } -Item *create_func_inet_aton(Item* a) + +Create_func_cot Create_func_cot::s_singleton; + +Item* +Create_func_cot::create(THD *thd, Item *arg1) { - return new Item_func_inet_aton(a); + Item *i1= new (thd->mem_root) Item_int((char*) "1", 1, 1); + Item *i2= new (thd->mem_root) Item_func_tan(arg1); + return new (thd->mem_root) Item_func_div(i1, i2); } -Item *create_func_ifnull(Item* a, Item *b) +Create_func_crc32 Create_func_crc32::s_singleton; + +Item* +Create_func_crc32::create(THD *thd, Item *arg1) { - return new Item_func_ifnull(a,b); + return new (thd->mem_root) Item_func_crc32(arg1); } -Item *create_func_nullif(Item* a, Item *b) + +#ifdef HAVE_SPATIAL +Create_func_crosses Create_func_crosses::s_singleton; + +Item* +Create_func_crosses::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_nullif(a,b); + return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2, + Item_func::SP_CROSSES_FUNC); } +#endif + -Item *create_func_locate(Item* a, Item *b) +Create_func_date_format Create_func_date_format::s_singleton; + +Item* +Create_func_date_format::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_locate(b,a); + return new (thd->mem_root) Item_func_date_format(arg1, arg2, 0); } -Item *create_func_instr(Item* a, Item *b) + +Create_func_datediff Create_func_datediff::s_singleton; + +Item* +Create_func_datediff::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_locate(a,b); + Item *i1= new (thd->mem_root) Item_func_to_days(arg1); + Item *i2= new (thd->mem_root) Item_func_to_days(arg2); + + return new (thd->mem_root) Item_func_minus(i1, i2); } -Item *create_func_isnull(Item* a) + +Create_func_dayname Create_func_dayname::s_singleton; + +Item* +Create_func_dayname::create(THD *thd, Item *arg1) { - return new Item_func_isnull(a); + return new (thd->mem_root) Item_func_dayname(arg1); } -Item *create_func_lcase(Item* a) + +Create_func_dayofmonth Create_func_dayofmonth::s_singleton; + +Item* +Create_func_dayofmonth::create(THD *thd, Item *arg1) { - return new Item_func_lcase(a); + return new (thd->mem_root) Item_func_dayofmonth(arg1); } -Item *create_func_length(Item* a) + +Create_func_dayofweek Create_func_dayofweek::s_singleton; + +Item* +Create_func_dayofweek::create(THD *thd, Item *arg1) { - return new Item_func_length(a); + return new (thd->mem_root) Item_func_weekday(arg1, 1); } -Item *create_func_bit_length(Item* a) + +Create_func_dayofyear Create_func_dayofyear::s_singleton; + +Item* +Create_func_dayofyear::create(THD *thd, Item *arg1) { - return new Item_func_bit_length(a); + return new (thd->mem_root) Item_func_dayofyear(arg1); } -Item *create_func_coercibility(Item* a) + +Create_func_decode Create_func_decode::s_singleton; + +Item* +Create_func_decode::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_coercibility(a); + return new (thd->mem_root) Item_func_decode(arg1, arg2); } -Item *create_func_char_length(Item* a) + +Create_func_degrees Create_func_degrees::s_singleton; + +Item* +Create_func_degrees::create(THD *thd, Item *arg1) { - return new Item_func_char_length(a); + return new (thd->mem_root) Item_func_units((char*) "degrees", arg1, + 180/M_PI, 0.0); } -Item *create_func_ln(Item* a) + +Create_func_des_decrypt Create_func_des_decrypt::s_singleton; + +Item* +Create_func_des_decrypt::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - return new Item_func_ln(a); + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 1: + { + Item *param_1= item_list->pop(); + func= new (thd->mem_root) Item_func_des_decrypt(param_1); + break; + } + case 2: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + func= new (thd->mem_root) Item_func_des_decrypt(param_1, param_2); + break; + } + default: + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + break; + } + } + + return func; } -Item *create_func_log2(Item* a) + +Create_func_des_encrypt Create_func_des_encrypt::s_singleton; + +Item* +Create_func_des_encrypt::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - return new Item_func_log2(a); + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 1: + { + Item *param_1= item_list->pop(); + func= new (thd->mem_root) Item_func_des_encrypt(param_1); + break; + } + case 2: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + func= new (thd->mem_root) Item_func_des_encrypt(param_1, param_2); + break; + } + default: + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + break; + } + } + + return func; } -Item *create_func_log10(Item* a) + +#ifdef HAVE_SPATIAL +Create_func_dimension Create_func_dimension::s_singleton; + +Item* +Create_func_dimension::create(THD *thd, Item *arg1) { - return new Item_func_log10(a); + return new (thd->mem_root) Item_func_dimension(arg1); } +#endif + -Item *create_func_lpad(Item* a, Item *b, Item *c) +#ifdef HAVE_SPATIAL +Create_func_disjoint Create_func_disjoint::s_singleton; + +Item* +Create_func_disjoint::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_lpad(a,b,c); + return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2, + Item_func::SP_DISJOINT_FUNC); } +#endif + -Item *create_func_ltrim(Item* a) +Create_func_elt Create_func_elt::s_singleton; + +Item* +Create_func_elt::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - return new Item_func_ltrim(a); + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + if (arg_count < 2) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + return NULL; + } + + return new (thd->mem_root) Item_func_elt(*item_list); } -Item *create_func_md5(Item* a) + +Create_func_encode Create_func_encode::s_singleton; + +Item* +Create_func_encode::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_md5(a); + return new (thd->mem_root) Item_func_encode(arg1, arg2); } -Item *create_func_mod(Item* a, Item *b) + +Create_func_encrypt Create_func_encrypt::s_singleton; + +Item* +Create_func_encrypt::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - return new Item_func_mod(a,b); + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 1: + { + Item *param_1= item_list->pop(); + func= new (thd->mem_root) Item_func_encrypt(param_1); + thd->lex->uncacheable(UNCACHEABLE_RAND); + break; + } + case 2: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + func= new (thd->mem_root) Item_func_encrypt(param_1, param_2); + break; + } + default: + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + break; + } + } + + return func; } -Item *create_func_name_const(Item *a, Item *b) + +#ifdef HAVE_SPATIAL +Create_func_endpoint Create_func_endpoint::s_singleton; + +Item* +Create_func_endpoint::create(THD *thd, Item *arg1) { - return new Item_name_const(a,b); + return new (thd->mem_root) Item_func_spatial_decomp(arg1, + Item_func::SP_ENDPOINT); } +#endif + + +#ifdef HAVE_SPATIAL +Create_func_envelope Create_func_envelope::s_singleton; -Item *create_func_monthname(Item* a) +Item* +Create_func_envelope::create(THD *thd, Item *arg1) { - return new Item_func_monthname(a); + return new (thd->mem_root) Item_func_envelope(arg1); } +#endif + + +#ifdef HAVE_SPATIAL +Create_func_equals Create_func_equals::s_singleton; -Item *create_func_month(Item* a) +Item* +Create_func_equals::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_month(a); + return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2, + Item_func::SP_EQUALS_FUNC); } +#endif + -Item *create_func_oct(Item *a) +Create_func_exp Create_func_exp::s_singleton; + +Item* +Create_func_exp::create(THD *thd, Item *arg1) { - return new Item_func_conv(a,new Item_int((int32) 10,2), - new Item_int((int32) 8,1)); + return new (thd->mem_root) Item_func_exp(arg1); } -Item *create_func_period_add(Item* a, Item *b) + +Create_func_export_set Create_func_export_set::s_singleton; + +Item* +Create_func_export_set::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - return new Item_func_period_add(a,b); + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 3: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + Item *param_3= item_list->pop(); + func= new (thd->mem_root) Item_func_export_set(param_1, param_2, param_3); + break; + } + case 4: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + Item *param_3= item_list->pop(); + Item *param_4= item_list->pop(); + func= new (thd->mem_root) Item_func_export_set(param_1, param_2, param_3, + param_4); + break; + } + case 5: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + Item *param_3= item_list->pop(); + Item *param_4= item_list->pop(); + Item *param_5= item_list->pop(); + func= new (thd->mem_root) Item_func_export_set(param_1, param_2, param_3, + param_4, param_5); + break; + } + default: + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + break; + } + } + + return func; } -Item *create_func_period_diff(Item* a, Item *b) + +#ifdef HAVE_SPATIAL +Create_func_exteriorring Create_func_exteriorring::s_singleton; + +Item* +Create_func_exteriorring::create(THD *thd, Item *arg1) { - return new Item_func_period_diff(a,b); + return new (thd->mem_root) Item_func_spatial_decomp(arg1, + Item_func::SP_EXTERIORRING); } +#endif + -Item *create_func_pi(void) +Create_func_field Create_func_field::s_singleton; + +Item* +Create_func_field::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - return new Item_static_float_func("pi()", M_PI, 6, 8); + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + if (arg_count < 2) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + return NULL; + } + + return new (thd->mem_root) Item_func_field(*item_list); } -Item *create_func_pow(Item* a, Item *b) + +Create_func_find_in_set Create_func_find_in_set::s_singleton; + +Item* +Create_func_find_in_set::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_pow(a,b); + return new (thd->mem_root) Item_func_find_in_set(arg1, arg2); } -Item *create_func_radians(Item *a) + +Create_func_floor Create_func_floor::s_singleton; + +Item* +Create_func_floor::create(THD *thd, Item *arg1) { - return new Item_func_units((char*) "radians",a,M_PI/180,0.0); + return new (thd->mem_root) Item_func_floor(arg1); } -Item *create_func_release_lock(Item* a) + +Create_func_format Create_func_format::s_singleton; + +Item* +Create_func_format::create(THD *thd, Item *arg1, Item *arg2) { - current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - return new Item_func_release_lock(a); + return new (thd->mem_root) Item_func_format(arg1, arg2); } -Item *create_func_repeat(Item* a, Item *b) + +Create_func_found_rows Create_func_found_rows::s_singleton; + +Item* +Create_func_found_rows::create(THD *thd) { - return new Item_func_repeat(a,b); + thd->lex->safe_to_cache_query= 0; + return new (thd->mem_root) Item_func_found_rows(); } -Item *create_func_reverse(Item* a) + +Create_func_from_days Create_func_from_days::s_singleton; + +Item* +Create_func_from_days::create(THD *thd, Item *arg1) { - return new Item_func_reverse(a); + return new (thd->mem_root) Item_func_from_days(arg1); } -Item *create_func_rpad(Item* a, Item *b, Item *c) + +Create_func_from_unixtime Create_func_from_unixtime::s_singleton; + +Item* +Create_func_from_unixtime::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - return new Item_func_rpad(a,b,c); + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 1: + { + Item *param_1= item_list->pop(); + func= new (thd->mem_root) Item_func_from_unixtime(param_1); + break; + } + case 2: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + Item *ut= new (thd->mem_root) Item_func_from_unixtime(param_1); + func= new (thd->mem_root) Item_func_date_format(ut, param_2, 0); + break; + } + default: + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + break; + } + } + + return func; } -Item *create_func_rtrim(Item* a) + +#ifdef HAVE_SPATIAL +Create_func_geometry_from_text Create_func_geometry_from_text::s_singleton; + +Item* +Create_func_geometry_from_text::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - return new Item_func_rtrim(a); + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 1: + { + Item *param_1= item_list->pop(); + func= new (thd->mem_root) Item_func_geometry_from_text(param_1); + thd->lex->uncacheable(UNCACHEABLE_RAND); + break; + } + case 2: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + func= new (thd->mem_root) Item_func_geometry_from_text(param_1, param_2); + break; + } + default: + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + break; + } + } + + return func; } +#endif -Item *create_func_sec_to_time(Item* a) + +#ifdef HAVE_SPATIAL +Create_func_geometry_from_wkb Create_func_geometry_from_wkb::s_singleton; + +Item* +Create_func_geometry_from_wkb::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - return new Item_func_sec_to_time(a); + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 1: + { + Item *param_1= item_list->pop(); + func= new (thd->mem_root) Item_func_geometry_from_wkb(param_1); + thd->lex->uncacheable(UNCACHEABLE_RAND); + break; + } + case 2: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + func= new (thd->mem_root) Item_func_geometry_from_wkb(param_1, param_2); + break; + } + default: + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + break; + } + } + + return func; } +#endif + + +#ifdef HAVE_SPATIAL +Create_func_geometry_type Create_func_geometry_type::s_singleton; -Item *create_func_sign(Item* a) +Item* +Create_func_geometry_type::create(THD *thd, Item *arg1) { - return new Item_func_sign(a); + return new (thd->mem_root) Item_func_geometry_type(arg1); } +#endif + + +#ifdef HAVE_SPATIAL +Create_func_geometryn Create_func_geometryn::s_singleton; -Item *create_func_sin(Item* a) +Item* +Create_func_geometryn::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_sin(a); + return new (thd->mem_root) Item_func_spatial_decomp_n(arg1, arg2, + Item_func::SP_GEOMETRYN); } +#endif + + +Create_func_get_lock Create_func_get_lock::s_singleton; -Item *create_func_sha(Item* a) +Item* +Create_func_get_lock::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_sha(a); + thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + return new (thd->mem_root) Item_func_get_lock(arg1, arg2); } -Item *create_func_sleep(Item* a) + +#ifdef HAVE_SPATIAL +Create_func_glength Create_func_glength::s_singleton; + +Item* +Create_func_glength::create(THD *thd, Item *arg1) { - current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - return new Item_func_sleep(a); + return new (thd->mem_root) Item_func_glength(arg1); } +#endif + + +Create_func_greatest Create_func_greatest::s_singleton; -Item *create_func_space(Item *a) +Item* +Create_func_greatest::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - CHARSET_INFO *cs= current_thd->variables.collation_connection; - Item *sp; + int arg_count= 0; - if (cs->mbminlen > 1) + if (item_list != NULL) + arg_count= item_list->elements; + + if (arg_count < 2) { - uint dummy_errors; - sp= new Item_string("",0,cs); - if (sp) - sp->str_value.copy(" ", 1, &my_charset_latin1, cs, &dummy_errors); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + return NULL; } - else + + return new (thd->mem_root) Item_func_max(*item_list); +} + + +Create_func_hex Create_func_hex::s_singleton; + +Item* +Create_func_hex::create(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_hex(arg1); +} + + +Create_func_ifnull Create_func_ifnull::s_singleton; + +Item* +Create_func_ifnull::create(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_ifnull(arg1, arg2); +} + + +Create_func_inet_ntoa Create_func_inet_ntoa::s_singleton; + +Item* +Create_func_inet_ntoa::create(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_inet_ntoa(arg1); +} + + +Create_func_inet_aton Create_func_inet_aton::s_singleton; + +Item* +Create_func_inet_aton::create(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_inet_aton(arg1); +} + + +Create_func_instr Create_func_instr::s_singleton; + +Item* +Create_func_instr::create(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_locate(arg1, arg2); +} + + +#ifdef HAVE_SPATIAL +Create_func_interiorringn Create_func_interiorringn::s_singleton; + +Item* +Create_func_interiorringn::create(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_spatial_decomp_n(arg1, arg2, + Item_func::SP_INTERIORRINGN); +} +#endif + + +#ifdef HAVE_SPATIAL +Create_func_intersects Create_func_intersects::s_singleton; + +Item* +Create_func_intersects::create(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2, + Item_func::SP_INTERSECTS_FUNC); +} +#endif + + +Create_func_is_free_lock Create_func_is_free_lock::s_singleton; + +Item* +Create_func_is_free_lock::create(THD *thd, Item *arg1) +{ + thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + return new (thd->mem_root) Item_func_is_free_lock(arg1); +} + + +Create_func_is_used_lock Create_func_is_used_lock::s_singleton; + +Item* +Create_func_is_used_lock::create(THD *thd, Item *arg1) +{ + thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + return new (thd->mem_root) Item_func_is_used_lock(arg1); +} + + +#ifdef HAVE_SPATIAL +Create_func_isclosed Create_func_isclosed::s_singleton; + +Item* +Create_func_isclosed::create(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_isclosed(arg1); +} +#endif + + +#ifdef HAVE_SPATIAL +Create_func_isempty Create_func_isempty::s_singleton; + +Item* +Create_func_isempty::create(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_isempty(arg1); +} +#endif + + +Create_func_isnull Create_func_isnull::s_singleton; + +Item* +Create_func_isnull::create(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_isnull(arg1); +} + + +#ifdef HAVE_SPATIAL +Create_func_issimple Create_func_issimple::s_singleton; + +Item* +Create_func_issimple::create(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_issimple(arg1); +} +#endif + + +Create_func_last_day Create_func_last_day::s_singleton; + +Item* +Create_func_last_day::create(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_last_day(arg1); +} + + +Create_func_last_insert_id Create_func_last_insert_id::s_singleton; + +Item* +Create_func_last_insert_id::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) +{ + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 0: { - sp= new Item_string(" ",1,cs); + func= new (thd->mem_root) Item_func_last_insert_id(); + thd->lex->safe_to_cache_query= 0; + break; } - return sp ? new Item_func_repeat(sp, a) : 0; + case 1: + { + Item *param_1= item_list->pop(); + func= new (thd->mem_root) Item_func_last_insert_id(param_1); + thd->lex->safe_to_cache_query= 0; + break; + } + default: + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + break; + } + } + + return func; } -Item *create_func_soundex(Item* a) + +Create_func_lcase Create_func_lcase::s_singleton; + +Item* +Create_func_lcase::create(THD *thd, Item *arg1) { - return new Item_func_soundex(a); + return new (thd->mem_root) Item_func_lcase(arg1); } -Item *create_func_sqrt(Item* a) + +Create_func_least Create_func_least::s_singleton; + +Item* +Create_func_least::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - return new Item_func_sqrt(a); + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + if (arg_count < 2) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + return NULL; + } + + return new (thd->mem_root) Item_func_min(*item_list); } -Item *create_func_strcmp(Item* a, Item *b) + +Create_func_length Create_func_length::s_singleton; + +Item* +Create_func_length::create(THD *thd, Item *arg1) { - return new Item_func_strcmp(a,b); + return new (thd->mem_root) Item_func_length(arg1); } -Item *create_func_tan(Item* a) + +Create_func_ln Create_func_ln::s_singleton; + +Item* +Create_func_ln::create(THD *thd, Item *arg1) { - return new Item_func_tan(a); + return new (thd->mem_root) Item_func_ln(arg1); } -Item *create_func_time_format(Item *a, Item *b) + +Create_func_load_file Create_func_load_file::s_singleton; + +Item* +Create_func_load_file::create(THD *thd, Item *arg1) { - return new Item_func_date_format(a,b,1); + thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + return new (thd->mem_root) Item_load_file(arg1); } -Item *create_func_time_to_sec(Item* a) + +Create_func_locate Create_func_locate::s_singleton; + +Item* +Create_func_locate::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - return new Item_func_time_to_sec(a); + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 2: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + /* Yes, parameters in that order : 2, 1 */ + func= new (thd->mem_root) Item_func_locate(param_2, param_1); + break; + } + case 3: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + Item *param_3= item_list->pop(); + /* Yes, parameters in that order : 2, 1, 3 */ + func= new (thd->mem_root) Item_func_locate(param_2, param_1, param_3); + break; + } + default: + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + break; + } + } + + return func; } -Item *create_func_to_days(Item* a) + +Create_func_log Create_func_log::s_singleton; + +Item* +Create_func_log::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - return new Item_func_to_days(a); + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 1: + { + Item *param_1= item_list->pop(); + func= new (thd->mem_root) Item_func_log(param_1); + break; + } + case 2: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + func= new (thd->mem_root) Item_func_log(param_1, param_2); + break; + } + default: + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + break; + } + } + + return func; } -Item *create_func_ucase(Item* a) + +Create_func_log10 Create_func_log10::s_singleton; + +Item* +Create_func_log10::create(THD *thd, Item *arg1) { - return new Item_func_ucase(a); + return new (thd->mem_root) Item_func_log10(arg1); } -Item *create_func_unhex(Item* a) + +Create_func_log2 Create_func_log2::s_singleton; + +Item* +Create_func_log2::create(THD *thd, Item *arg1) { - return new Item_func_unhex(a); + return new (thd->mem_root) Item_func_log2(arg1); } -Item *create_func_uuid(void) + +Create_func_lpad Create_func_lpad::s_singleton; + +Item* +Create_func_lpad::create(THD *thd, Item *arg1, Item *arg2, Item *arg3) { - THD *thd= current_thd; -#ifdef HAVE_ROW_BASED_REPLICATION - thd->lex->binlog_row_based_if_mixed= TRUE; -#endif - return new(thd->mem_root) Item_func_uuid(); + return new (thd->mem_root) Item_func_lpad(arg1, arg2, arg3); } -Item *create_func_version(void) + +Create_func_ltrim Create_func_ltrim::s_singleton; + +Item* +Create_func_ltrim::create(THD *thd, Item *arg1) { - return new Item_static_string_func("version()", server_version, - (uint) strlen(server_version), - system_charset_info, DERIVATION_SYSCONST); + return new (thd->mem_root) Item_func_ltrim(arg1); } -Item *create_func_weekday(Item* a) + +Create_func_makedate Create_func_makedate::s_singleton; + +Item* +Create_func_makedate::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_weekday(a, 0); + return new (thd->mem_root) Item_func_makedate(arg1, arg2); } -Item *create_func_year(Item* a) + +Create_func_maketime Create_func_maketime::s_singleton; + +Item* +Create_func_maketime::create(THD *thd, Item *arg1, Item *arg2, Item *arg3) { - return new Item_func_year(a); + return new (thd->mem_root) Item_func_maketime(arg1, arg2, arg3); } -Item *create_load_file(Item* a) + +Create_func_make_set Create_func_make_set::s_singleton; + +Item* +Create_func_make_set::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - return new Item_load_file(a); + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + if (arg_count < 2) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + return NULL; + } + + Item *param_1= item_list->pop(); + return new (thd->mem_root) Item_func_make_set(param_1, *item_list); } -Item *create_func_cast(Item *a, Cast_target cast_type, int len, int dec, - CHARSET_INFO *cs) +Create_func_master_pos_wait Create_func_master_pos_wait::s_singleton; + +Item* +Create_func_master_pos_wait::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - Item *res; - int tmp_len; - LINT_INIT(res); + Item *func= NULL; + int arg_count= 0; - switch (cast_type) { - case ITEM_CAST_BINARY: res= new Item_func_binary(a); break; - case ITEM_CAST_SIGNED_INT: res= new Item_func_signed(a); break; - case ITEM_CAST_UNSIGNED_INT: res= new Item_func_unsigned(a); break; - case ITEM_CAST_DATE: res= new Item_date_typecast(a); break; - case ITEM_CAST_TIME: res= new Item_time_typecast(a); break; - case ITEM_CAST_DATETIME: res= new Item_datetime_typecast(a); break; - case ITEM_CAST_DECIMAL: - tmp_len= (len>0) ? len : 10; - if (tmp_len < dec) - { - my_error(ER_M_BIGGER_THAN_D, MYF(0), ""); - return 0; - } - res= new Item_decimal_typecast(a, tmp_len, dec); + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 2: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + func= new (thd->mem_root) Item_master_pos_wait(param_1, param_2); + thd->lex->safe_to_cache_query= 0; break; - case ITEM_CAST_CHAR: - res= new Item_char_typecast(a, len, cs ? cs : - current_thd->variables.collation_connection); + } + case 3: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + Item *param_3= item_list->pop(); + func= new (thd->mem_root) Item_master_pos_wait(param_1, param_2, param_3); + thd->lex->safe_to_cache_query= 0; break; + } default: - DBUG_ASSERT(0); - res= 0; + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); break; } - return res; + } + + return func; } -Item *create_func_is_free_lock(Item* a) + +Create_func_md5 Create_func_md5::s_singleton; + +Item* +Create_func_md5::create(THD *thd, Item *arg1) { - current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - return new Item_func_is_free_lock(a); + return new (thd->mem_root) Item_func_md5(arg1); } -Item *create_func_is_used_lock(Item* a) + +Create_func_monthname Create_func_monthname::s_singleton; + +Item* +Create_func_monthname::create(THD *thd, Item *arg1) { - current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - return new Item_func_is_used_lock(a); + return new (thd->mem_root) Item_func_monthname(arg1); } -Item *create_func_quote(Item* a) + +Create_func_name_const Create_func_name_const::s_singleton; + +Item* +Create_func_name_const::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_quote(a); + return new (thd->mem_root) Item_name_const(arg1, arg2); } -Item *create_func_xml_extractvalue(Item *a, Item *b) + +Create_func_nullif Create_func_nullif::s_singleton; + +Item* +Create_func_nullif::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_xml_extractvalue(a, b); + return new (thd->mem_root) Item_func_nullif(arg1, arg2); } -Item *create_func_xml_update(Item *a, Item *b, Item *c) + +#ifdef HAVE_SPATIAL +Create_func_numgeometries Create_func_numgeometries::s_singleton; + +Item* +Create_func_numgeometries::create(THD *thd, Item *arg1) { - return new Item_func_xml_update(a, b, c); + return new (thd->mem_root) Item_func_numgeometries(arg1); } +#endif + #ifdef HAVE_SPATIAL -Item *create_func_as_wkt(Item *a) +Create_func_numinteriorring Create_func_numinteriorring::s_singleton; + +Item* +Create_func_numinteriorring::create(THD *thd, Item *arg1) { - return new Item_func_as_wkt(a); + return new (thd->mem_root) Item_func_numinteriorring(arg1); } +#endif -Item *create_func_as_wkb(Item *a) + +#ifdef HAVE_SPATIAL +Create_func_numpoints Create_func_numpoints::s_singleton; + +Item* +Create_func_numpoints::create(THD *thd, Item *arg1) { - return new Item_func_as_wkb(a); + return new (thd->mem_root) Item_func_numpoints(arg1); } +#endif + -Item *create_func_srid(Item *a) +Create_func_oct Create_func_oct::s_singleton; + +Item* +Create_func_oct::create(THD *thd, Item *arg1) { - return new Item_func_srid(a); + Item *i10= new (thd->mem_root) Item_int((int32) 10,2); + Item *i8= new (thd->mem_root) Item_int((int32) 8,1); + return new (thd->mem_root) Item_func_conv(arg1, i10, i8); } -Item *create_func_startpoint(Item *a) + +Create_func_ord Create_func_ord::s_singleton; + +Item* +Create_func_ord::create(THD *thd, Item *arg1) { - return new Item_func_spatial_decomp(a, Item_func::SP_STARTPOINT); + return new (thd->mem_root) Item_func_ord(arg1); } -Item *create_func_endpoint(Item *a) + +#ifdef HAVE_SPATIAL +Create_func_overlaps Create_func_overlaps::s_singleton; + +Item* +Create_func_overlaps::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_spatial_decomp(a, Item_func::SP_ENDPOINT); + return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2, + Item_func::SP_OVERLAPS_FUNC); } +#endif + -Item *create_func_exteriorring(Item *a) +Create_func_period_add Create_func_period_add::s_singleton; + +Item* +Create_func_period_add::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_spatial_decomp(a, Item_func::SP_EXTERIORRING); + return new (thd->mem_root) Item_func_period_add(arg1, arg2); } -Item *create_func_pointn(Item *a, Item *b) + +Create_func_period_diff Create_func_period_diff::s_singleton; + +Item* +Create_func_period_diff::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_spatial_decomp_n(a, b, Item_func::SP_POINTN); + return new (thd->mem_root) Item_func_period_diff(arg1, arg2); } -Item *create_func_interiorringn(Item *a, Item *b) + +Create_func_pi Create_func_pi::s_singleton; + +Item* +Create_func_pi::create(THD *thd) { - return new Item_func_spatial_decomp_n(a, b, Item_func::SP_INTERIORRINGN); + return new (thd->mem_root) Item_static_float_func("pi()", M_PI, 6, 8); } -Item *create_func_geometryn(Item *a, Item *b) + +#ifdef HAVE_SPATIAL +Create_func_pointn Create_func_pointn::s_singleton; + +Item* +Create_func_pointn::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_spatial_decomp_n(a, b, Item_func::SP_GEOMETRYN); + return new (thd->mem_root) Item_func_spatial_decomp_n(arg1, arg2, + Item_func::SP_POINTN); } +#endif -Item *create_func_centroid(Item *a) + +Create_func_pow Create_func_pow::s_singleton; + +Item* +Create_func_pow::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_centroid(a); + return new (thd->mem_root) Item_func_pow(arg1, arg2); } -Item *create_func_envelope(Item *a) + +Create_func_quote Create_func_quote::s_singleton; + +Item* +Create_func_quote::create(THD *thd, Item *arg1) { - return new Item_func_envelope(a); + return new (thd->mem_root) Item_func_quote(arg1); } -Item *create_func_equals(Item *a, Item *b) + +Create_func_radians Create_func_radians::s_singleton; + +Item* +Create_func_radians::create(THD *thd, Item *arg1) { - return new Item_func_spatial_rel(a, b, Item_func::SP_EQUALS_FUNC); + return new (thd->mem_root) Item_func_units((char*) "radians", arg1, + M_PI/180, 0.0); } -Item *create_func_disjoint(Item *a, Item *b) + +Create_func_rand Create_func_rand::s_singleton; + +Item* +Create_func_rand::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - return new Item_func_spatial_rel(a, b, Item_func::SP_DISJOINT_FUNC); + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 0: + { + func= new (thd->mem_root) Item_func_rand(); + thd->lex->uncacheable(UNCACHEABLE_RAND); + break; + } + case 1: + { + Item *param_1= item_list->pop(); + func= new (thd->mem_root) Item_func_rand(param_1); + thd->lex->uncacheable(UNCACHEABLE_RAND); + break; + } + default: + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + break; + } + } + + return func; } -Item *create_func_intersects(Item *a, Item *b) + +Create_func_release_lock Create_func_release_lock::s_singleton; + +Item* +Create_func_release_lock::create(THD *thd, Item *arg1) { - return new Item_func_spatial_rel(a, b, Item_func::SP_INTERSECTS_FUNC); + thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + return new (thd->mem_root) Item_func_release_lock(arg1); } -Item *create_func_touches(Item *a, Item *b) + +Create_func_reverse Create_func_reverse::s_singleton; + +Item* +Create_func_reverse::create(THD *thd, Item *arg1) { - return new Item_func_spatial_rel(a, b, Item_func::SP_TOUCHES_FUNC); + return new (thd->mem_root) Item_func_reverse(arg1); } -Item *create_func_crosses(Item *a, Item *b) + +Create_func_round Create_func_round::s_singleton; + +Item* +Create_func_round::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - return new Item_func_spatial_rel(a, b, Item_func::SP_CROSSES_FUNC); + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 1: + { + Item *param_1= item_list->pop(); + Item *i0 = new (thd->mem_root) Item_int((char*)"0", 0, 1); + func= new (thd->mem_root) Item_func_round(param_1, i0, 0); + break; + } + case 2: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + func= new (thd->mem_root) Item_func_round(param_1, param_2, 0); + break; + } + default: + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + break; + } + } + + return func; } -Item *create_func_within(Item *a, Item *b) + +Create_func_row_count Create_func_row_count::s_singleton; + +Item* +Create_func_row_count::create(THD *thd) { - return new Item_func_spatial_rel(a, b, Item_func::SP_WITHIN_FUNC); + thd->lex->safe_to_cache_query= 0; + return new (thd->mem_root) Item_func_row_count(); } -Item *create_func_contains(Item *a, Item *b) + +Create_func_rpad Create_func_rpad::s_singleton; + +Item* +Create_func_rpad::create(THD *thd, Item *arg1, Item *arg2, Item *arg3) { - return new Item_func_spatial_rel(a, b, Item_func::SP_CONTAINS_FUNC); + return new (thd->mem_root) Item_func_rpad(arg1, arg2, arg3); } -Item *create_func_overlaps(Item *a, Item *b) + +Create_func_rtrim Create_func_rtrim::s_singleton; + +Item* +Create_func_rtrim::create(THD *thd, Item *arg1) { - return new Item_func_spatial_rel(a, b, Item_func::SP_OVERLAPS_FUNC); + return new (thd->mem_root) Item_func_rtrim(arg1); } -Item *create_func_isempty(Item *a) + +Create_func_sec_to_time Create_func_sec_to_time::s_singleton; + +Item* +Create_func_sec_to_time::create(THD *thd, Item *arg1) { - return new Item_func_isempty(a); + return new (thd->mem_root) Item_func_sec_to_time(arg1); } -Item *create_func_issimple(Item *a) + +Create_func_sha Create_func_sha::s_singleton; + +Item* +Create_func_sha::create(THD *thd, Item *arg1) { - return new Item_func_issimple(a); + return new (thd->mem_root) Item_func_sha(arg1); } -Item *create_func_isclosed(Item *a) + +Create_func_sign Create_func_sign::s_singleton; + +Item* +Create_func_sign::create(THD *thd, Item *arg1) { - return new Item_func_isclosed(a); + return new (thd->mem_root) Item_func_sign(arg1); } -Item *create_func_geometry_type(Item *a) + +Create_func_sin Create_func_sin::s_singleton; + +Item* +Create_func_sin::create(THD *thd, Item *arg1) { - return new Item_func_geometry_type(a); + return new (thd->mem_root) Item_func_sin(arg1); } -Item *create_func_dimension(Item *a) + +Create_func_sleep Create_func_sleep::s_singleton; + +Item* +Create_func_sleep::create(THD *thd, Item *arg1) { - return new Item_func_dimension(a); + thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + return new (thd->mem_root) Item_func_sleep(arg1); } -Item *create_func_x(Item *a) + +Create_func_soundex Create_func_soundex::s_singleton; + +Item* +Create_func_soundex::create(THD *thd, Item *arg1) { - return new Item_func_x(a); + return new (thd->mem_root) Item_func_soundex(arg1); } -Item *create_func_y(Item *a) + +Create_func_space Create_func_space::s_singleton; + +Item* +Create_func_space::create(THD *thd, Item *arg1) { - return new Item_func_y(a); + /** + TODO: Fix Bug#23637 + The parsed item tree should not depend on + <code>thd->variables.collation_connection</code>. + */ + CHARSET_INFO *cs= thd->variables.collation_connection; + Item *sp; + + if (cs->mbminlen > 1) + { + uint dummy_errors; + sp= new (thd->mem_root) Item_string("", 0, cs); + sp->str_value.copy(" ", 1, &my_charset_latin1, cs, &dummy_errors); + } + else + { + sp= new (thd->mem_root) Item_string(" ", 1, cs); + } + + return new (thd->mem_root) Item_func_repeat(sp, arg1); +} + + +Create_func_sqrt Create_func_sqrt::s_singleton; + +Item* +Create_func_sqrt::create(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_sqrt(arg1); +} + + +#ifdef HAVE_SPATIAL +Create_func_srid Create_func_srid::s_singleton; + +Item* +Create_func_srid::create(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_srid(arg1); } +#endif + + +#ifdef HAVE_SPATIAL +Create_func_startpoint Create_func_startpoint::s_singleton; + +Item* +Create_func_startpoint::create(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_spatial_decomp(arg1, + Item_func::SP_STARTPOINT); +} +#endif + + +Create_func_str_to_date Create_func_str_to_date::s_singleton; + +Item* +Create_func_str_to_date::create(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_str_to_date(arg1, arg2); +} + + +Create_func_strcmp Create_func_strcmp::s_singleton; -Item *create_func_numpoints(Item *a) +Item* +Create_func_strcmp::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_numpoints(a); + return new (thd->mem_root) Item_func_strcmp(arg1, arg2); } -Item *create_func_numinteriorring(Item *a) + +Create_func_substr_index Create_func_substr_index::s_singleton; + +Item* +Create_func_substr_index::create(THD *thd, Item *arg1, Item *arg2, Item *arg3) { - return new Item_func_numinteriorring(a); + return new (thd->mem_root) Item_func_substr_index(arg1, arg2, arg3); } -Item *create_func_numgeometries(Item *a) + +Create_func_subtime Create_func_subtime::s_singleton; + +Item* +Create_func_subtime::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_numgeometries(a); + return new (thd->mem_root) Item_func_add_time(arg1, arg2, 0, 1); } -Item *create_func_area(Item *a) + +Create_func_tan Create_func_tan::s_singleton; + +Item* +Create_func_tan::create(THD *thd, Item *arg1) { - return new Item_func_area(a); + return new (thd->mem_root) Item_func_tan(arg1); } -Item *create_func_glength(Item *a) + +Create_func_time_format Create_func_time_format::s_singleton; + +Item* +Create_func_time_format::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_glength(a); + return new (thd->mem_root) Item_func_date_format(arg1, arg2, 1); } -Item *create_func_point(Item *a, Item *b) + +Create_func_time_to_sec Create_func_time_to_sec::s_singleton; + +Item* +Create_func_time_to_sec::create(THD *thd, Item *arg1) { - return new Item_func_point(a, b); + return new (thd->mem_root) Item_func_time_to_sec(arg1); } -#endif /*HAVE_SPATIAL*/ -Item *create_func_crc32(Item* a) + +Create_func_timediff Create_func_timediff::s_singleton; + +Item* +Create_func_timediff::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_crc32(a); + return new (thd->mem_root) Item_func_timediff(arg1, arg2); } -Item *create_func_compress(Item* a) + +Create_func_to_days Create_func_to_days::s_singleton; + +Item* +Create_func_to_days::create(THD *thd, Item *arg1) { - return new Item_func_compress(a); + return new (thd->mem_root) Item_func_to_days(arg1); } -Item *create_func_uncompress(Item* a) + +#ifdef HAVE_SPATIAL +Create_func_touches Create_func_touches::s_singleton; + +Item* +Create_func_touches::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_uncompress(a); + return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2, + Item_func::SP_TOUCHES_FUNC); } +#endif -Item *create_func_uncompressed_length(Item* a) + +Create_func_ucase Create_func_ucase::s_singleton; + +Item* +Create_func_ucase::create(THD *thd, Item *arg1) { - return new Item_func_uncompressed_length(a); + return new (thd->mem_root) Item_func_ucase(arg1); } -Item *create_func_datediff(Item *a, Item *b) + +Create_func_uncompress Create_func_uncompress::s_singleton; + +Item* +Create_func_uncompress::create(THD *thd, Item *arg1) { - return new Item_func_minus(new Item_func_to_days(a), - new Item_func_to_days(b)); + return new (thd->mem_root) Item_func_uncompress(arg1); } -Item *create_func_weekofyear(Item *a) + +Create_func_uncompressed_length Create_func_uncompressed_length::s_singleton; + +Item* +Create_func_uncompressed_length::create(THD *thd, Item *arg1) { - return new Item_func_week(a, new Item_int((char*) "0", 3, 1)); + return new (thd->mem_root) Item_func_uncompressed_length(arg1); } -Item *create_func_makedate(Item* a,Item* b) + +Create_func_unhex Create_func_unhex::s_singleton; + +Item* +Create_func_unhex::create(THD *thd, Item *arg1) { - return new Item_func_makedate(a, b); + return new (thd->mem_root) Item_func_unhex(arg1); } -Item *create_func_addtime(Item* a,Item* b) + +Create_func_unix_timestamp Create_func_unix_timestamp::s_singleton; + +Item* +Create_func_unix_timestamp::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) { - return new Item_func_add_time(a, b, 0, 0); + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 0: + { + func= new (thd->mem_root) Item_func_unix_timestamp(); + thd->lex->safe_to_cache_query= 0; + break; + } + case 1: + { + Item *param_1= item_list->pop(); + func= new (thd->mem_root) Item_func_unix_timestamp(param_1); + break; + } + default: + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + break; + } + } + + return func; } -Item *create_func_subtime(Item* a,Item* b) + +Create_func_uuid Create_func_uuid::s_singleton; + +Item* +Create_func_uuid::create(THD *thd) { - return new Item_func_add_time(a, b, 0, 1); + thd->lex->binlog_row_based_if_mixed= TRUE; + return new (thd->mem_root) Item_func_uuid(); } -Item *create_func_timediff(Item* a,Item* b) + +Create_func_version Create_func_version::s_singleton; + +Item* +Create_func_version::create(THD *thd) { - return new Item_func_timediff(a, b); + return new (thd->mem_root) Item_static_string_func("version()", + server_version, + (uint) strlen(server_version), + system_charset_info, + DERIVATION_SYSCONST); } -Item *create_func_maketime(Item* a,Item* b,Item* c) + +Create_func_weekday Create_func_weekday::s_singleton; + +Item* +Create_func_weekday::create(THD *thd, Item *arg1) { - return new Item_func_maketime(a, b, c); + return new (thd->mem_root) Item_func_weekday(arg1, 0); } -Item *create_func_str_to_date(Item* a,Item* b) + +Create_func_weekofyear Create_func_weekofyear::s_singleton; + +Item* +Create_func_weekofyear::create(THD *thd, Item *arg1) { - return new Item_func_str_to_date(a, b); + Item *i1= new (thd->mem_root) Item_int((char*) "0", 3, 1); + return new (thd->mem_root) Item_func_week(arg1, i1); } -Item *create_func_last_day(Item *a) + +#ifdef HAVE_SPATIAL +Create_func_within Create_func_within::s_singleton; + +Item* +Create_func_within::create(THD *thd, Item *arg1, Item *arg2) { - return new Item_func_last_day(a); + return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2, + Item_func::SP_WITHIN_FUNC); } +#endif + + +#ifdef HAVE_SPATIAL +Create_func_x Create_func_x::s_singleton; + +Item* +Create_func_x::create(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_x(arg1); +} +#endif + + +Create_func_xml_extractvalue Create_func_xml_extractvalue::s_singleton; + +Item* +Create_func_xml_extractvalue::create(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_xml_extractvalue(arg1, arg2); +} + + +Create_func_xml_update Create_func_xml_update::s_singleton; + +Item* +Create_func_xml_update::create(THD *thd, Item *arg1, Item *arg2, Item *arg3) +{ + return new (thd->mem_root) Item_func_xml_update(arg1, arg2, arg3); +} + + +#ifdef HAVE_SPATIAL +Create_func_y Create_func_y::s_singleton; + +Item* +Create_func_y::create(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_y(arg1); +} +#endif + + +Create_func_year_week Create_func_year_week::s_singleton; + +Item* +Create_func_year_week::create_native(THD *thd, LEX_STRING name, + List<Item> *item_list) +{ + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 1: + { + Item *param_1= item_list->pop(); + Item *i0= new (thd->mem_root) Item_int((char*) "0", 0, 1); + func= new (thd->mem_root) Item_func_yearweek(param_1, i0); + break; + } + case 2: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + func= new (thd->mem_root) Item_func_yearweek(param_1, param_2); + break; + } + default: + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + break; + } + } + + return func; +} + + +struct Native_func_registry +{ + LEX_STRING name; + Create_func *builder; +}; + +#define BUILDER(F) & F::s_singleton + +#ifdef HAVE_SPATIAL + #define GEOM_BUILDER(F) & F::s_singleton +#else + #define GEOM_BUILDER(F) & Create_func_no_geom::s_singleton +#endif + +/* + MySQL native functions. + MAINTAINER: + - Keep sorted for human lookup. At runtime, a hash table is used. + - do **NOT** conditionally (#ifdef, #ifndef) define a function *NAME*: + doing so will cause user code that works against a --without-XYZ binary + to fail with name collisions against a --with-XYZ binary. + Use something similar to GEOM_BUILDER instead. + - keep 1 line per entry, it makes grep | sort easier +*/ + +static Native_func_registry func_array[] = +{ + { C_STRING_WITH_LEN("ABS"), BUILDER(Create_func_abs)}, + { C_STRING_WITH_LEN("ACOS"), BUILDER(Create_func_acos)}, + { C_STRING_WITH_LEN("ADDTIME"), BUILDER(Create_func_addtime)}, + { C_STRING_WITH_LEN("AES_DECRYPT"), BUILDER(Create_func_aes_decrypt)}, + { C_STRING_WITH_LEN("AES_ENCRYPT"), BUILDER(Create_func_aes_encrypt)}, + { C_STRING_WITH_LEN("AREA"), GEOM_BUILDER(Create_func_area)}, + { C_STRING_WITH_LEN("ASBINARY"), GEOM_BUILDER(Create_func_as_wkb)}, + { C_STRING_WITH_LEN("ASIN"), BUILDER(Create_func_asin)}, + { C_STRING_WITH_LEN("ASTEXT"), GEOM_BUILDER(Create_func_as_wkt)}, + { C_STRING_WITH_LEN("ASWKB"), GEOM_BUILDER(Create_func_as_wkb)}, + { C_STRING_WITH_LEN("ASWKT"), GEOM_BUILDER(Create_func_as_wkt)}, + { C_STRING_WITH_LEN("ATAN"), BUILDER(Create_func_atan)}, + { C_STRING_WITH_LEN("ATAN2"), BUILDER(Create_func_atan)}, + { C_STRING_WITH_LEN("BENCHMARK"), BUILDER(Create_func_benchmark)}, + { C_STRING_WITH_LEN("BIN"), BUILDER(Create_func_bin)}, + { C_STRING_WITH_LEN("BIT_COUNT"), BUILDER(Create_func_bit_count)}, + { C_STRING_WITH_LEN("BIT_LENGTH"), BUILDER(Create_func_bit_length)}, + { C_STRING_WITH_LEN("CEIL"), BUILDER(Create_func_ceiling)}, + { C_STRING_WITH_LEN("CEILING"), BUILDER(Create_func_ceiling)}, + { C_STRING_WITH_LEN("CENTROID"), GEOM_BUILDER(Create_func_centroid)}, + { C_STRING_WITH_LEN("CHARACTER_LENGTH"), BUILDER(Create_func_char_length)}, + { C_STRING_WITH_LEN("CHAR_LENGTH"), BUILDER(Create_func_char_length)}, + { C_STRING_WITH_LEN("COERCIBILITY"), BUILDER(Create_func_coercibility)}, + { C_STRING_WITH_LEN("COMPRESS"), BUILDER(Create_func_compress)}, + { C_STRING_WITH_LEN("CONCAT"), BUILDER(Create_func_concat)}, + { C_STRING_WITH_LEN("CONCAT_WS"), BUILDER(Create_func_concat_ws)}, + { C_STRING_WITH_LEN("CONNECTION_ID"), BUILDER(Create_func_connection_id)}, + { C_STRING_WITH_LEN("CONV"), BUILDER(Create_func_conv)}, + { C_STRING_WITH_LEN("CONVERT_TZ"), BUILDER(Create_func_convert_tz)}, + { C_STRING_WITH_LEN("COS"), BUILDER(Create_func_cos)}, + { C_STRING_WITH_LEN("COT"), BUILDER(Create_func_cot)}, + { C_STRING_WITH_LEN("CRC32"), BUILDER(Create_func_crc32)}, + { C_STRING_WITH_LEN("CROSSES"), GEOM_BUILDER(Create_func_crosses)}, + { C_STRING_WITH_LEN("DATEDIFF"), BUILDER(Create_func_datediff)}, + { C_STRING_WITH_LEN("DATE_FORMAT"), BUILDER(Create_func_date_format)}, + { C_STRING_WITH_LEN("DAYNAME"), BUILDER(Create_func_dayname)}, + { C_STRING_WITH_LEN("DAYOFMONTH"), BUILDER(Create_func_dayofmonth)}, + { C_STRING_WITH_LEN("DAYOFWEEK"), BUILDER(Create_func_dayofweek)}, + { C_STRING_WITH_LEN("DAYOFYEAR"), BUILDER(Create_func_dayofyear)}, + { C_STRING_WITH_LEN("DECODE"), BUILDER(Create_func_decode)}, + { C_STRING_WITH_LEN("DEGREES"), BUILDER(Create_func_degrees)}, + { C_STRING_WITH_LEN("DES_DECRYPT"), BUILDER(Create_func_des_decrypt)}, + { C_STRING_WITH_LEN("DES_ENCRYPT"), BUILDER(Create_func_des_encrypt)}, + { C_STRING_WITH_LEN("DIMENSION"), GEOM_BUILDER(Create_func_dimension)}, + { C_STRING_WITH_LEN("DISJOINT"), GEOM_BUILDER(Create_func_disjoint)}, + { C_STRING_WITH_LEN("ELT"), BUILDER(Create_func_elt)}, + { C_STRING_WITH_LEN("ENCODE"), BUILDER(Create_func_encode)}, + { C_STRING_WITH_LEN("ENCRYPT"), BUILDER(Create_func_encrypt)}, + { C_STRING_WITH_LEN("ENDPOINT"), GEOM_BUILDER(Create_func_endpoint)}, + { C_STRING_WITH_LEN("ENVELOPE"), GEOM_BUILDER(Create_func_envelope)}, + { C_STRING_WITH_LEN("EQUALS"), GEOM_BUILDER(Create_func_equals)}, + { C_STRING_WITH_LEN("EXP"), BUILDER(Create_func_exp)}, + { C_STRING_WITH_LEN("EXPORT_SET"), BUILDER(Create_func_export_set)}, + { C_STRING_WITH_LEN("EXTERIORRING"), GEOM_BUILDER(Create_func_exteriorring)}, + { C_STRING_WITH_LEN("EXTRACTVALUE"), BUILDER(Create_func_xml_extractvalue)}, + { C_STRING_WITH_LEN("FIELD"), BUILDER(Create_func_field)}, + { C_STRING_WITH_LEN("FIND_IN_SET"), BUILDER(Create_func_find_in_set)}, + { C_STRING_WITH_LEN("FLOOR"), BUILDER(Create_func_floor)}, + { C_STRING_WITH_LEN("FORMAT"), BUILDER(Create_func_format)}, + { C_STRING_WITH_LEN("FOUND_ROWS"), BUILDER(Create_func_found_rows)}, + { C_STRING_WITH_LEN("FROM_DAYS"), BUILDER(Create_func_from_days)}, + { C_STRING_WITH_LEN("FROM_UNIXTIME"), BUILDER(Create_func_from_unixtime)}, + { C_STRING_WITH_LEN("GEOMCOLLFROMTEXT"), GEOM_BUILDER(Create_func_geometry_from_text)}, + { C_STRING_WITH_LEN("GEOMCOLLFROMWKB"), GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { C_STRING_WITH_LEN("GEOMETRYCOLLECTIONFROMTEXT"), GEOM_BUILDER(Create_func_geometry_from_text)}, + { C_STRING_WITH_LEN("GEOMETRYCOLLECTIONFROMWKB"), GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { C_STRING_WITH_LEN("GEOMETRYFROMTEXT"), GEOM_BUILDER(Create_func_geometry_from_text)}, + { C_STRING_WITH_LEN("GEOMETRYFROMWKB"), GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { C_STRING_WITH_LEN("GEOMETRYN"), GEOM_BUILDER(Create_func_geometryn)}, + { C_STRING_WITH_LEN("GEOMETRYTYPE"), GEOM_BUILDER(Create_func_geometry_type)}, + { C_STRING_WITH_LEN("GEOMFROMTEXT"), GEOM_BUILDER(Create_func_geometry_from_text)}, + { C_STRING_WITH_LEN("GEOMFROMWKB"), GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { C_STRING_WITH_LEN("GET_LOCK"), BUILDER(Create_func_get_lock)}, + { C_STRING_WITH_LEN("GLENGTH"), GEOM_BUILDER(Create_func_glength)}, + { C_STRING_WITH_LEN("GREATEST"), BUILDER(Create_func_greatest)}, + { C_STRING_WITH_LEN("HEX"), BUILDER(Create_func_hex)}, + { C_STRING_WITH_LEN("IFNULL"), BUILDER(Create_func_ifnull)}, + { C_STRING_WITH_LEN("INET_ATON"), BUILDER(Create_func_inet_aton)}, + { C_STRING_WITH_LEN("INET_NTOA"), BUILDER(Create_func_inet_ntoa)}, + { C_STRING_WITH_LEN("INSTR"), BUILDER(Create_func_instr)}, + { C_STRING_WITH_LEN("INTERIORRINGN"), GEOM_BUILDER(Create_func_interiorringn)}, + { C_STRING_WITH_LEN("INTERSECTS"), GEOM_BUILDER(Create_func_intersects)}, + { C_STRING_WITH_LEN("ISCLOSED"), GEOM_BUILDER(Create_func_isclosed)}, + { C_STRING_WITH_LEN("ISEMPTY"), GEOM_BUILDER(Create_func_isempty)}, + { C_STRING_WITH_LEN("ISNULL"), BUILDER(Create_func_isnull)}, + { C_STRING_WITH_LEN("ISSIMPLE"), GEOM_BUILDER(Create_func_issimple)}, + { C_STRING_WITH_LEN("IS_FREE_LOCK"), BUILDER(Create_func_is_free_lock)}, + { C_STRING_WITH_LEN("IS_USED_LOCK"), BUILDER(Create_func_is_used_lock)}, + { C_STRING_WITH_LEN("LAST_DAY"), BUILDER(Create_func_last_day)}, + { C_STRING_WITH_LEN("LAST_INSERT_ID"), BUILDER(Create_func_last_insert_id)}, + { C_STRING_WITH_LEN("LCASE"), BUILDER(Create_func_lcase)}, + { C_STRING_WITH_LEN("LEAST"), BUILDER(Create_func_least)}, + { C_STRING_WITH_LEN("LENGTH"), BUILDER(Create_func_length)}, + { C_STRING_WITH_LEN("LINEFROMTEXT"), GEOM_BUILDER(Create_func_geometry_from_text)}, + { C_STRING_WITH_LEN("LINEFROMWKB"), GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { C_STRING_WITH_LEN("LINESTRINGFROMTEXT"), GEOM_BUILDER(Create_func_geometry_from_text)}, + { C_STRING_WITH_LEN("LINESTRINGFROMWKB"), GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { C_STRING_WITH_LEN("LN"), BUILDER(Create_func_ln)}, + { C_STRING_WITH_LEN("LOAD_FILE"), BUILDER(Create_func_load_file)}, + { C_STRING_WITH_LEN("LOCATE"), BUILDER(Create_func_locate)}, + { C_STRING_WITH_LEN("LOG"), BUILDER(Create_func_log)}, + { C_STRING_WITH_LEN("LOG10"), BUILDER(Create_func_log10)}, + { C_STRING_WITH_LEN("LOG2"), BUILDER(Create_func_log2)}, + { C_STRING_WITH_LEN("LOWER"), BUILDER(Create_func_lcase)}, + { C_STRING_WITH_LEN("LPAD"), BUILDER(Create_func_lpad)}, + { C_STRING_WITH_LEN("LTRIM"), BUILDER(Create_func_ltrim)}, + { C_STRING_WITH_LEN("MAKEDATE"), BUILDER(Create_func_makedate)}, + { C_STRING_WITH_LEN("MAKETIME"), BUILDER(Create_func_maketime)}, + { C_STRING_WITH_LEN("MAKE_SET"), BUILDER(Create_func_make_set)}, + { C_STRING_WITH_LEN("MASTER_POS_WAIT"), BUILDER(Create_func_master_pos_wait)}, + { C_STRING_WITH_LEN("MBRCONTAINS"), GEOM_BUILDER(Create_func_contains)}, + { C_STRING_WITH_LEN("MD5"), BUILDER(Create_func_md5)}, + { C_STRING_WITH_LEN("MLINEFROMTEXT"), GEOM_BUILDER(Create_func_geometry_from_text)}, + { C_STRING_WITH_LEN("MLINEFROMWKB"), GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { C_STRING_WITH_LEN("MONTHNAME"), BUILDER(Create_func_monthname)}, + { C_STRING_WITH_LEN("MPOINTFROMTEXT"), GEOM_BUILDER(Create_func_geometry_from_text)}, + { C_STRING_WITH_LEN("MPOINTFROMWKB"), GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { C_STRING_WITH_LEN("MPOLYFROMTEXT"), GEOM_BUILDER(Create_func_geometry_from_text)}, + { C_STRING_WITH_LEN("MPOLYFROMWKB"), GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { C_STRING_WITH_LEN("MULTILINESTRINGFROMTEXT"), GEOM_BUILDER(Create_func_geometry_from_text)}, + { C_STRING_WITH_LEN("MULTILINESTRINGFROMWKB"), GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { C_STRING_WITH_LEN("MULTIPOINTFROMTEXT"), GEOM_BUILDER(Create_func_geometry_from_text)}, + { C_STRING_WITH_LEN("MULTIPOINTFROMWKB"), GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { C_STRING_WITH_LEN("MULTIPOLYGONFROMTEXT"), GEOM_BUILDER(Create_func_geometry_from_text)}, + { C_STRING_WITH_LEN("MULTIPOLYGONFROMWKB"), GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { C_STRING_WITH_LEN("NAME_CONST"), BUILDER(Create_func_name_const)}, + { C_STRING_WITH_LEN("NULLIF"), BUILDER(Create_func_nullif)}, + { C_STRING_WITH_LEN("NUMGEOMETRIES"), GEOM_BUILDER(Create_func_numgeometries)}, + { C_STRING_WITH_LEN("NUMINTERIORRINGS"), GEOM_BUILDER(Create_func_numinteriorring)}, + { C_STRING_WITH_LEN("NUMPOINTS"), GEOM_BUILDER(Create_func_numpoints)}, + { C_STRING_WITH_LEN("OCT"), BUILDER(Create_func_oct)}, + { C_STRING_WITH_LEN("OCTET_LENGTH"), BUILDER(Create_func_length)}, + { C_STRING_WITH_LEN("ORD"), BUILDER(Create_func_ord)}, + { C_STRING_WITH_LEN("OVERLAPS"), GEOM_BUILDER(Create_func_overlaps)}, + { C_STRING_WITH_LEN("PERIOD_ADD"), BUILDER(Create_func_period_add)}, + { C_STRING_WITH_LEN("PERIOD_DIFF"), BUILDER(Create_func_period_diff)}, + { C_STRING_WITH_LEN("PI"), BUILDER(Create_func_pi)}, + { C_STRING_WITH_LEN("POINTFROMTEXT"), GEOM_BUILDER(Create_func_geometry_from_text)}, + { C_STRING_WITH_LEN("POINTFROMWKB"), GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { C_STRING_WITH_LEN("POINTN"), GEOM_BUILDER(Create_func_pointn)}, + { C_STRING_WITH_LEN("POLYFROMTEXT"), GEOM_BUILDER(Create_func_geometry_from_text)}, + { C_STRING_WITH_LEN("POLYFROMWKB"), GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { C_STRING_WITH_LEN("POLYGONFROMTEXT"), GEOM_BUILDER(Create_func_geometry_from_text)}, + { C_STRING_WITH_LEN("POLYGONFROMWKB"), GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { C_STRING_WITH_LEN("POW"), BUILDER(Create_func_pow)}, + { C_STRING_WITH_LEN("POWER"), BUILDER(Create_func_pow)}, + { C_STRING_WITH_LEN("QUOTE"), BUILDER(Create_func_quote)}, + { C_STRING_WITH_LEN("RADIANS"), BUILDER(Create_func_radians)}, + { C_STRING_WITH_LEN("RAND"), BUILDER(Create_func_rand)}, + { C_STRING_WITH_LEN("RELEASE_LOCK"), BUILDER(Create_func_release_lock)}, + { C_STRING_WITH_LEN("REVERSE"), BUILDER(Create_func_reverse)}, + { C_STRING_WITH_LEN("ROUND"), BUILDER(Create_func_round)}, + { C_STRING_WITH_LEN("ROW_COUNT"), BUILDER(Create_func_row_count)}, + { C_STRING_WITH_LEN("RPAD"), BUILDER(Create_func_rpad)}, + { C_STRING_WITH_LEN("RTRIM"), BUILDER(Create_func_rtrim)}, + { C_STRING_WITH_LEN("SEC_TO_TIME"), BUILDER(Create_func_sec_to_time)}, + { C_STRING_WITH_LEN("SHA"), BUILDER(Create_func_sha)}, + { C_STRING_WITH_LEN("SHA1"), BUILDER(Create_func_sha)}, + { C_STRING_WITH_LEN("SIGN"), BUILDER(Create_func_sign)}, + { C_STRING_WITH_LEN("SIN"), BUILDER(Create_func_sin)}, + { C_STRING_WITH_LEN("SLEEP"), BUILDER(Create_func_sleep)}, + { C_STRING_WITH_LEN("SOUNDEX"), BUILDER(Create_func_soundex)}, + { C_STRING_WITH_LEN("SPACE"), BUILDER(Create_func_space)}, + { C_STRING_WITH_LEN("SQRT"), BUILDER(Create_func_sqrt)}, + { C_STRING_WITH_LEN("SRID"), GEOM_BUILDER(Create_func_srid)}, + { C_STRING_WITH_LEN("STARTPOINT"), GEOM_BUILDER(Create_func_startpoint)}, + { C_STRING_WITH_LEN("STRCMP"), BUILDER(Create_func_strcmp)}, + { C_STRING_WITH_LEN("STR_TO_DATE"), BUILDER(Create_func_str_to_date)}, + { C_STRING_WITH_LEN("SUBSTRING_INDEX"), BUILDER(Create_func_substr_index)}, + { C_STRING_WITH_LEN("SUBTIME"), BUILDER(Create_func_subtime)}, + { C_STRING_WITH_LEN("TAN"), BUILDER(Create_func_tan)}, + { C_STRING_WITH_LEN("TIMEDIFF"), BUILDER(Create_func_timediff)}, + { C_STRING_WITH_LEN("TIME_FORMAT"), BUILDER(Create_func_time_format)}, + { C_STRING_WITH_LEN("TIME_TO_SEC"), BUILDER(Create_func_time_to_sec)}, + { C_STRING_WITH_LEN("TOUCHES"), GEOM_BUILDER(Create_func_touches)}, + { C_STRING_WITH_LEN("TO_DAYS"), BUILDER(Create_func_to_days)}, + { C_STRING_WITH_LEN("UCASE"), BUILDER(Create_func_ucase)}, + { C_STRING_WITH_LEN("UNCOMPRESS"), BUILDER(Create_func_uncompress)}, + { C_STRING_WITH_LEN("UNCOMPRESSED_LENGTH"), BUILDER(Create_func_uncompressed_length)}, + { C_STRING_WITH_LEN("UNHEX"), BUILDER(Create_func_unhex)}, + { C_STRING_WITH_LEN("UNIX_TIMESTAMP"), BUILDER(Create_func_unix_timestamp)}, + { C_STRING_WITH_LEN("UPDATEXML"), BUILDER(Create_func_xml_update)}, + { C_STRING_WITH_LEN("UPPER"), BUILDER(Create_func_ucase)}, + { C_STRING_WITH_LEN("UUID"), BUILDER(Create_func_uuid)}, + { C_STRING_WITH_LEN("VERSION"), BUILDER(Create_func_version)}, + { C_STRING_WITH_LEN("WEEKDAY"), BUILDER(Create_func_weekday)}, + { C_STRING_WITH_LEN("WEEKOFYEAR"), BUILDER(Create_func_weekofyear)}, + { C_STRING_WITH_LEN("WITHIN"), GEOM_BUILDER(Create_func_within)}, + { C_STRING_WITH_LEN("X"), GEOM_BUILDER(Create_func_x)}, + { C_STRING_WITH_LEN("Y"), GEOM_BUILDER(Create_func_y)}, + { C_STRING_WITH_LEN("YEARWEEK"), BUILDER(Create_func_year_week)}, + + { {0, 0}, NULL} +}; + +static HASH native_functions_hash; + +extern "C" byte* +get_native_fct_hash_key(const byte *buff, uint *length, my_bool /* unused */) +{ + Native_func_registry *func= (Native_func_registry*) buff; + *length= func->name.length; + return (byte*) func->name.str; +} + +/* + Load the hash table for native functions. + Note: this code is not thread safe, and is intended to be used at server + startup only (before going multi-threaded) +*/ + +int item_create_init() +{ + Native_func_registry *func; + + DBUG_ENTER("item_create_init"); + + if (hash_init(& native_functions_hash, + system_charset_info, + array_elements(func_array), + 0, + 0, + (hash_get_key) get_native_fct_hash_key, + NULL, /* Nothing to free */ + MYF(0))) + DBUG_RETURN(1); + + for (func= func_array; func->builder != NULL; func++) + { + if (my_hash_insert(& native_functions_hash, (byte*) func)) + DBUG_RETURN(1); + } + +#ifndef DBUG_OFF + for (uint i=0 ; i < native_functions_hash.records ; i++) + { + func= (Native_func_registry*) hash_element(& native_functions_hash, i); + DBUG_PRINT("info", ("native function %s, length %d", + func->name.str, func->name.length)); + } +#endif + + DBUG_RETURN(0); +} + +/* + Empty the hash table for native functions. + Note: this code is not thread safe, and is intended to be used at server + shutdown only (after thread requests have been executed). +*/ + +void item_create_cleanup() +{ + DBUG_ENTER("item_create_cleanup"); + hash_free(& native_functions_hash); + DBUG_VOID_RETURN; +} + +Create_func * +find_native_function_builder(THD *thd, LEX_STRING name) +{ + Native_func_registry *func; + Create_func *builder= NULL; + + /* Thread safe */ + func= (Native_func_registry*) hash_search(& native_functions_hash, + (byte*) name.str, + name.length); + + if (func) + { + builder= func->builder; + } + + return builder; +} + +Create_qfunc * +find_qualified_function_builder(THD *thd) +{ + return & Create_sp_func::s_singleton; +} + +Item* +create_func_cast(THD *thd, Item *a, Cast_target cast_type, int len, int dec, + CHARSET_INFO *cs) +{ + Item *res; + LINT_INIT(res); + + switch (cast_type) { + case ITEM_CAST_BINARY: + res= new (thd->mem_root) Item_func_binary(a); + break; + case ITEM_CAST_SIGNED_INT: + res= new (thd->mem_root) Item_func_signed(a); + break; + case ITEM_CAST_UNSIGNED_INT: + res= new (thd->mem_root) Item_func_unsigned(a); + break; + case ITEM_CAST_DATE: + res= new (thd->mem_root) Item_date_typecast(a); + break; + case ITEM_CAST_TIME: + res= new (thd->mem_root) Item_time_typecast(a); + break; + case ITEM_CAST_DATETIME: + res= new (thd->mem_root) Item_datetime_typecast(a); + break; + case ITEM_CAST_DECIMAL: + { + int tmp_len= (len>0) ? len : 10; + if (tmp_len < dec) + { + my_error(ER_M_BIGGER_THAN_D, MYF(0), ""); + return 0; + } + res= new (thd->mem_root) Item_decimal_typecast(a, tmp_len, dec); + break; + } + case ITEM_CAST_CHAR: + { + CHARSET_INFO *real_cs= (cs ? cs : thd->variables.collation_connection); + res= new (thd->mem_root) Item_char_typecast(a, len, real_cs); + break; + } + default: + { + DBUG_ASSERT(0); + res= 0; + break; + } + } + return res; +} + diff --git a/sql/item_create.h b/sql/item_create.h index 9b6a74b5bdd..985c4428d8f 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -14,148 +13,154 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* Functions to create an item. Used by lex.h */ - -Item *create_func_abs(Item* a); -Item *create_func_acos(Item* a); -Item *create_func_aes_encrypt(Item* a, Item* b); -Item *create_func_aes_decrypt(Item* a, Item* b); -Item *create_func_ascii(Item* a); -Item *create_func_asin(Item* a); -Item *create_func_bin(Item* a); -Item *create_func_bit_count(Item* a); -Item *create_func_bit_length(Item* a); -Item *create_func_coercibility(Item* a); -Item *create_func_ceiling(Item* a); -Item *create_func_char_length(Item* a); -Item *create_func_cast(Item *a, Cast_target cast_type, int len, int dec, - CHARSET_INFO *cs); -Item *create_func_connection_id(void); -Item *create_func_conv(Item* a, Item *b, Item *c); -Item *create_func_cos(Item* a); -Item *create_func_cot(Item* a); -Item *create_func_crc32(Item* a); -Item *create_func_date_format(Item* a,Item *b); -Item *create_func_dayname(Item* a); -Item *create_func_dayofmonth(Item* a); -Item *create_func_dayofweek(Item* a); -Item *create_func_dayofyear(Item* a); -Item *create_func_degrees(Item *); -Item *create_func_exp(Item* a); -Item *create_func_find_in_set(Item* a, Item *b); -Item *create_func_floor(Item* a); -Item *create_func_found_rows(void); -Item *create_func_from_days(Item* a); -Item *create_func_get_lock(Item* a, Item *b); -Item *create_func_hex(Item *a); -Item *create_func_inet_aton(Item* a); -Item *create_func_inet_ntoa(Item* a); - -Item *create_func_ifnull(Item* a, Item *b); -Item *create_func_instr(Item* a, Item *b); -Item *create_func_isnull(Item* a); -Item *create_func_lcase(Item* a); -Item *create_func_length(Item* a); -Item *create_func_ln(Item* a); -Item *create_func_locate(Item* a, Item *b); -Item *create_func_log2(Item* a); -Item *create_func_log10(Item* a); -Item *create_func_lpad(Item* a, Item *b, Item *c); -Item *create_func_ltrim(Item* a); -Item *create_func_md5(Item* a); -Item *create_func_mod(Item* a, Item *b); -Item *create_func_monthname(Item* a); -Item *create_func_name_const(Item *a, Item *b); -Item *create_func_nullif(Item* a, Item *b); -Item *create_func_oct(Item *); -Item *create_func_ord(Item* a); -Item *create_func_period_add(Item* a, Item *b); -Item *create_func_period_diff(Item* a, Item *b); -Item *create_func_pi(void); -Item *create_func_pow(Item* a, Item *b); -Item *create_func_radians(Item *a); -Item *create_func_release_lock(Item* a); -Item *create_func_repeat(Item* a, Item *b); -Item *create_func_reverse(Item* a); -Item *create_func_rpad(Item* a, Item *b, Item *c); -Item *create_func_rtrim(Item* a); -Item *create_func_sec_to_time(Item* a); -Item *create_func_sign(Item* a); -Item *create_func_sin(Item* a); -Item *create_func_sha(Item* a); -Item *create_func_sleep(Item* a); -Item *create_func_soundex(Item* a); -Item *create_func_space(Item *); -Item *create_func_sqrt(Item* a); -Item *create_func_strcmp(Item* a, Item *b); -Item *create_func_tan(Item* a); -Item *create_func_time_format(Item *a, Item *b); -Item *create_func_time_to_sec(Item* a); -Item *create_func_to_days(Item* a); -Item *create_func_ucase(Item* a); -Item *create_func_unhex(Item* a); -Item *create_func_uuid(void); -Item *create_func_version(void); -Item *create_func_weekday(Item* a); -Item *create_load_file(Item* a); -Item *create_func_is_free_lock(Item* a); -Item *create_func_is_used_lock(Item* a); -Item *create_func_quote(Item* a); -Item *create_func_xml_extractvalue(Item *a, Item *b); -Item *create_func_xml_update(Item *a, Item *b, Item *c); -#ifdef HAVE_SPATIAL - -Item *create_func_geometry_from_text(Item *a); -Item *create_func_as_wkt(Item *a); -Item *create_func_as_wkb(Item *a); -Item *create_func_srid(Item *a); -Item *create_func_startpoint(Item *a); -Item *create_func_endpoint(Item *a); -Item *create_func_exteriorring(Item *a); -Item *create_func_centroid(Item *a); -Item *create_func_envelope(Item *a); -Item *create_func_pointn(Item *a, Item *b); -Item *create_func_interiorringn(Item *a, Item *b); -Item *create_func_geometryn(Item *a, Item *b); - -Item *create_func_equals(Item *a, Item *b); -Item *create_func_disjoint(Item *a, Item *b); -Item *create_func_intersects(Item *a, Item *b); -Item *create_func_touches(Item *a, Item *b); -Item *create_func_crosses(Item *a, Item *b); -Item *create_func_within(Item *a, Item *b); -Item *create_func_contains(Item *a, Item *b); -Item *create_func_overlaps(Item *a, Item *b); - -Item *create_func_isempty(Item *a); -Item *create_func_issimple(Item *a); -Item *create_func_isclosed(Item *a); - -Item *create_func_geometry_type(Item *a); -Item *create_func_dimension(Item *a); -Item *create_func_x(Item *a); -Item *create_func_y(Item *a); -Item *create_func_area(Item *a); -Item *create_func_glength(Item *a); - -Item *create_func_numpoints(Item *a); -Item *create_func_numinteriorring(Item *a); -Item *create_func_numgeometries(Item *a); - -Item *create_func_point(Item *a, Item *b); - -#endif /*HAVE_SPATIAL*/ - -Item *create_func_compress(Item *a); -Item *create_func_uncompress(Item *a); -Item *create_func_uncompressed_length(Item *a); - -Item *create_func_datediff(Item *a, Item *b); -Item *create_func_weekofyear(Item *a); -Item *create_func_makedate(Item* a,Item* b); -Item *create_func_addtime(Item* a,Item* b); -Item *create_func_subtime(Item* a,Item* b); -Item *create_func_timediff(Item* a,Item* b); -Item *create_func_maketime(Item* a,Item* b,Item* c); -Item *create_func_str_to_date(Item* a,Item* b); -Item *create_func_last_day(Item *a); +/* Functions to create an item. Used by sql/sql_yacc.yy */ + +#ifndef ITEM_CREATE_H +#define ITEM_CREATE_H + +/** + Public function builder interface. + The parser (sql/sql_yacc.yy) uses a factory / builder pattern to + construct an <code>Item</code> object for each function call. + All the concrete function builders implements this interface, + either directly or indirectly with some adapter helpers. + Keeping the function creation separated from the bison grammar allows + to simplify the parser, and avoid the need to introduce a new token + for each function, which has undesirable side effects in the grammar. +*/ + +class Create_func +{ +public: + /** + The builder create method. + Given the function name and list or arguments, this method creates + an <code>Item</code> that represents the function call. + In case or errors, a NULL item is returned, and an error is reported. + Note that the <code>thd</code> object may be modified by the builder. + In particular, the following members/methods can be set/called, + depending on the function called and the function possible side effects. + <ul> + <li><code>thd->lex->binlog_row_based_if_mixed</code></li> + <li><code>thd->lex->current_context()</code></li> + <li><code>thd->lex->safe_to_cache_query</code></li> + <li><code>thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT)</code></li> + <li><code>thd->lex->uncacheable(UNCACHEABLE_RAND)</code></li> + <li><code>thd->lex->add_time_zone_tables_to_query_tables(thd)</code></li> + </ul> + @param thd The current thread + @param name The function name + @param item_list The list of arguments to the function, can be NULL + @return An item representing the parsed function call, or NULL + */ + virtual Item* create(THD *thd, LEX_STRING name, List<Item> *item_list) = 0; + +protected: + /** Constructor */ + Create_func() {} + /** Destructor */ + virtual ~Create_func() {} +}; + + +/** + Function builder for qualified functions. + This builder is used with functions call using a qualified function name + syntax, as in <code>db.func(expr, expr, ...)</code>. +*/ + +class Create_qfunc : public Create_func +{ +public: + /** + The builder create method, for unqualified functions. + This builder will use the current database for the database name. + @param thd The current thread + @param name The function name + @param item_list The list of arguments to the function, can be NULL + @return An item representing the parsed function call + */ + virtual Item* create(THD *thd, LEX_STRING name, List<Item> *item_list); + + /** + The builder create method, for qualified functions. + @param thd The current thread + @param db The database name + @param name The function name + @param item_list The list of arguments to the function, can be NULL + @return An item representing the parsed function call + */ + virtual Item* create(THD *thd, LEX_STRING db, LEX_STRING name, + List<Item> *item_list) = 0; + +protected: + /** Constructor. */ + Create_qfunc() {} + /** Destructor. */ + virtual ~Create_qfunc() {} +}; + + +/** + Find the native function builder associated with a given function name. + @param thd The current thread + @param name The native function name + @return The native function builder associated with the name, or NULL +*/ +extern Create_func * find_native_function_builder(THD *thd, LEX_STRING name); + + +/** + Find the function builder for qualified functions. + @param thd The current thread + @return A function builder for qualified functions +*/ +extern Create_qfunc * find_qualified_function_builder(THD *thd); + + +#ifdef HAVE_DLOPEN +/** + Function builder for User Defined Functions. +*/ + +class Create_udf_func : public Create_func +{ +public: + virtual Item* create(THD *thd, LEX_STRING name, List<Item> *item_list); + + /** + The builder create method, for User Defined Functions. + @param thd The current thread + @param fct The User Defined Function metadata + @param item_list The list of arguments to the function, can be NULL + @return An item representing the parsed function call + */ + Item* create(THD *thd, udf_func *fct, List<Item> *item_list); + + /** Singleton. */ + static Create_udf_func s_singleton; + +protected: + /** Constructor. */ + Create_udf_func() {} + /** Destructor. */ + virtual ~Create_udf_func() {} +}; +#endif + + +/** + Builder for cast expressions. + @param thd The current thread + @param a The item to cast + @param cast_type the type casted into + @param len TODO + @param dec TODO + @param cs The character set +*/ +Item* +create_func_cast(THD *thd, Item *a, Cast_target cast_type, int len, int dec, + CHARSET_INFO *cs); + +#endif + diff --git a/sql/item_func.cc b/sql/item_func.cc index 4a069e662f9..c823575b240 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -105,7 +104,7 @@ Item_func::Item_func(THD *thd, Item_func *item) /* - Resolve references to table column for a function and it's argument + Resolve references to table column for a function and its argument SYNOPSIS: fix_fields() @@ -233,6 +232,8 @@ void Item_func::traverse_cond(Cond_traverser traverser, (*traverser)(this, argument); } } + else + (*traverser)(this, argument); } @@ -412,8 +413,13 @@ bool Item_func::eq(const Item *item, bool binary_cmp) const if (item->type() != FUNC_ITEM) return 0; Item_func *item_func=(Item_func*) item; - if (arg_count != item_func->arg_count || - func_name() != item_func->func_name()) + Item_func::Functype func_type; + if ((func_type= functype()) != item_func->functype() || + arg_count != item_func->arg_count || + (func_type != Item_func::FUNC_SP && + func_name() != item_func->func_name()) || + (func_type == Item_func::FUNC_SP && + my_strcasecmp(system_charset_info, func_name(), item_func->func_name()))) return 0; for (uint i=0; i < arg_count ; i++) if (!args[i]->eq(item_func->args[i], binary_cmp)) @@ -897,7 +903,8 @@ void Item_func_signed::print(String *str) longlong Item_func_signed::val_int_from_str(int *error) { - char buff[MAX_FIELD_WIDTH], *end; + char buff[MAX_FIELD_WIDTH], *end, *start; + uint32 length; String tmp(buff,sizeof(buff), &my_charset_bin), *res; longlong value; @@ -913,13 +920,21 @@ longlong Item_func_signed::val_int_from_str(int *error) return 0; } null_value= 0; - end= (char*) res->ptr()+ res->length(); - value= my_strtoll10(res->ptr(), &end, error); - if (*error > 0 || end != res->ptr()+ res->length()) + start= (char *)res->ptr(); + length= res->length(); + + end= start + length; + value= my_strtoll10(start, &end, error); + if (*error > 0 || end != start+ length) + { + char err_buff[128]; + String err_tmp(err_buff,(uint32) sizeof(err_buff), system_charset_info); + err_tmp.copy(start, length, system_charset_info); push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER", - res->c_ptr()); + err_tmp.c_ptr()); + } return value; } @@ -961,7 +976,14 @@ longlong Item_func_unsigned::val_int() longlong value; int error; - if (args[0]->cast_to_int_type() != STRING_RESULT) + if (args[0]->cast_to_int_type() == DECIMAL_RESULT) + { + my_decimal tmp, *dec= args[0]->val_decimal(&tmp); + if (!(null_value= args[0]->null_value)) + my_decimal2int(E_DEC_FATAL_ERROR, dec, 1, &value); + return value; + } + else if (args[0]->cast_to_int_type() != STRING_RESULT) { value= args[0]->val_int(); null_value= args[0]->null_value; @@ -982,7 +1004,7 @@ String *Item_decimal_typecast::val_str(String *str) my_decimal tmp_buf, *tmp= val_decimal(&tmp_buf); if (null_value) return NULL; - my_decimal2string(E_DEC_FATAL_ERROR, &tmp_buf, 0, 0, 0, str); + my_decimal2string(E_DEC_FATAL_ERROR, tmp, 0, 0, 0, str); return str; } @@ -1346,6 +1368,10 @@ longlong Item_func_mod::int_op() signal_divide_by_null(); return 0; } + + if (args[0]->unsigned_flag) + return ((ulonglong) value) % val2; + return value % val2; } @@ -1397,6 +1423,13 @@ void Item_func_mod::result_precision() } +void Item_func_mod::fix_length_and_dec() +{ + Item_num_op::fix_length_and_dec(); + maybe_null= 1; +} + + double Item_func_neg::real_op() { double value= args[0]->val_real(); @@ -2016,6 +2049,18 @@ my_decimal *Item_func_round::decimal_op(my_decimal *decimal_value) } +void Item_func_rand::seed_random(Item *arg) +{ + /* + TODO: do not do reinit 'rand' for every execute of PS/SP if + args[0] is a constant. + */ + uint32 tmp= (uint32) arg->val_int(); + randominit(rand, (uint32) (tmp*0x10001L+55555555L), + (uint32) (tmp*0x10000001L)); +} + + bool Item_func_rand::fix_fields(THD *thd,Item **ref) { if (Item_real_func::fix_fields(thd, ref)) @@ -2023,11 +2068,6 @@ bool Item_func_rand::fix_fields(THD *thd,Item **ref) used_tables_cache|= RAND_TABLE_BIT; if (arg_count) { // Only use argument once in query - if (!args[0]->const_during_execution()) - { - my_error(ER_WRONG_ARGUMENTS, MYF(0), "RAND"); - return TRUE; - } /* Allocate rand structure once: we must use thd->stmt_arena to create rand in proper mem_root if it's a prepared statement or @@ -2039,20 +2079,9 @@ bool Item_func_rand::fix_fields(THD *thd,Item **ref) if (!rand && !(rand= (struct rand_struct*) thd->stmt_arena->alloc(sizeof(*rand)))) return TRUE; - /* - PARAM_ITEM is returned if we're in statement prepare and consequently - no placeholder value is set yet. - */ - if (args[0]->type() != PARAM_ITEM) - { - /* - TODO: do not do reinit 'rand' for every execute of PS/SP if - args[0] is a constant. - */ - uint32 tmp= (uint32) args[0]->val_int(); - randominit(rand, (uint32) (tmp*0x10001L+55555555L), - (uint32) (tmp*0x10000001L)); - } + + if (args[0]->const_item()) + seed_random (args[0]); } else { @@ -2082,6 +2111,8 @@ void Item_func_rand::update_used_tables() double Item_func_rand::val_real() { DBUG_ASSERT(fixed == 1); + if (arg_count && !args[0]->const_item()) + seed_random (args[0]); return my_rnd(rand); } @@ -2323,28 +2354,35 @@ longlong Item_func_locate::val_int() return 0; /* purecov: inspected */ } null_value=0; - uint start=0; - uint start0=0; + /* must be longlong to avoid truncation */ + longlong start= 0; + longlong start0= 0; my_match_t match; if (arg_count == 3) { - start0= start =(uint) args[2]->val_int()-1; - start=a->charpos(start); - - if (start > a->length() || start+b->length() > a->length()) + start0= start= args[2]->val_int() - 1; + + if ((start < 0) || (start > a->length())) + return 0; + + /* start is now sufficiently valid to pass to charpos function */ + start= a->charpos((int) start); + + if (start + b->length() > a->length()) return 0; } if (!b->length()) // Found empty string at start - return (longlong) (start+1); + return start + 1; if (!cmp_collation.collation->coll->instr(cmp_collation.collation, - a->ptr()+start, a->length()-start, + a->ptr()+start, + (uint) (a->length()-start), b->ptr(), b->length(), &match, 1)) return 0; - return (longlong) match.mblen + start0 + 1; + return (longlong) match.mb_len + start0 + 1; } @@ -2479,7 +2517,7 @@ void Item_func_find_in_set::fix_length_and_dec() if (args[0]->const_item() && args[1]->type() == FIELD_ITEM) { Field *field= ((Item_field*) args[1])->field; - if (field->real_type() == FIELD_TYPE_SET) + if (field->real_type() == MYSQL_TYPE_SET) { String *find=args[0]->val_str(&value); if (find) @@ -2700,39 +2738,50 @@ udf_handler::fix_fields(THD *thd, Item_result_field *func, char *to=num_buffer; for (uint i=0; i < arg_count; i++) { - f_args.args[i]=0; + /* + For a constant argument i, args->args[i] points to the argument value. + For non-constant, args->args[i] is NULL. + */ + f_args.args[i]= NULL; /* Non-const unless updated below. */ + f_args.lengths[i]= arguments[i]->max_length; f_args.maybe_null[i]= (char) arguments[i]->maybe_null; f_args.attributes[i]= arguments[i]->name; f_args.attribute_lengths[i]= arguments[i]->name_length; - switch(arguments[i]->type()) { - case Item::STRING_ITEM: // Constant string ! + if (arguments[i]->const_item()) { - String *res=arguments[i]->val_str(&buffers[i]); - if (arguments[i]->null_value) - continue; - f_args.args[i]= (char*) res->ptr(); - break; - } - case Item::INT_ITEM: - *((longlong*) to) = arguments[i]->val_int(); - if (!arguments[i]->null_value) - { - f_args.args[i]=to; - to+= ALIGN_SIZE(sizeof(longlong)); - } - break; - case Item::REAL_ITEM: - *((double*) to)= arguments[i]->val_real(); - if (!arguments[i]->null_value) - { - f_args.args[i]=to; - to+= ALIGN_SIZE(sizeof(double)); - } - break; - default: // Skip these - break; + switch (arguments[i]->result_type()) + { + case STRING_RESULT: + case DECIMAL_RESULT: + { + String *res= arguments[i]->val_str(&buffers[i]); + if (arguments[i]->null_value) + continue; + f_args.args[i]= (char*) res->ptr(); + break; + } + case INT_RESULT: + *((longlong*) to)= arguments[i]->val_int(); + if (arguments[i]->null_value) + continue; + f_args.args[i]= to; + to+= ALIGN_SIZE(sizeof(longlong)); + break; + case REAL_RESULT: + *((double*) to)= arguments[i]->val_real(); + if (arguments[i]->null_value) + continue; + f_args.args[i]= to; + to+= ALIGN_SIZE(sizeof(double)); + break; + case ROW_RESULT: + default: + // This case should never be chosen + DBUG_ASSERT(0); + break; + } } } thd->net.last_error[0]=0; @@ -2886,6 +2935,20 @@ void Item_udf_func::cleanup() } +void Item_udf_func::print(String *str) +{ + str->append(func_name()); + str->append('('); + for (uint i=0 ; i < arg_count ; i++) + { + if (i != 0) + str->append(','); + args[i]->print_item_w_name(str); + } + str->append(')'); +} + + double Item_func_udf_float::val_real() { DBUG_ASSERT(fixed == 1); @@ -3029,8 +3092,8 @@ public: int count; bool locked; pthread_cond_t cond; - pthread_t thread; - ulong thread_id; + my_thread_id thread_id; + void set_thread(THD *thd) { thread_id= thd->thread_id; } User_level_lock(const char *key_arg,uint length, ulong id) :key_length(length),count(1),locked(1), thread_id(id) @@ -3117,9 +3180,9 @@ longlong Item_master_pos_wait::val_int() null_value = 1; return 0; } +#ifdef HAVE_REPLICATION longlong pos = (ulong)args[1]->val_int(); longlong timeout = (arg_count==3) ? args[2]->val_int() : 0 ; -#ifdef HAVE_REPLICATION if ((event_count = active_mi->rli.wait_for_pos(thd, log_name, pos, timeout)) == -2) { null_value = 1; @@ -3183,7 +3246,7 @@ void debug_sync_point(const char* lock_name, uint lock_timeout) else { ull->locked=1; - ull->thread=thd->real_id; + ull->set_thread(thd); thd->ull=ull; } pthread_mutex_unlock(&LOCK_user_locks); @@ -3258,7 +3321,7 @@ longlong Item_func_get_lock::val_int() null_value=1; // Probably out of memory return 0; } - ull->thread=thd->real_id; + ull->set_thread(thd); thd->ull=ull; pthread_mutex_unlock(&LOCK_user_locks); return 1; // Got new lock @@ -3299,7 +3362,7 @@ longlong Item_func_get_lock::val_int() else // We got the lock { ull->locked=1; - ull->thread=thd->real_id; + ull->set_thread(thd); ull->thread_id= thd->thread_id; thd->ull=ull; error=0; @@ -3347,7 +3410,7 @@ longlong Item_func_release_lock::val_int() } else { - if (ull->locked && pthread_equal(pthread_self(),ull->thread)) + if (ull->locked && current_thd->thread_id == ull->thread_id) { result=1; // Release is ok item_user_lock_release(ull); @@ -3389,19 +3452,33 @@ longlong Item_func_benchmark::val_int() DBUG_ASSERT(fixed == 1); char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff), &my_charset_bin); + my_decimal tmp_decimal; THD *thd=current_thd; + ulong loop_count; + + loop_count= (ulong) args[0]->val_int(); + if (args[0]->null_value) + { + null_value= 1; + return 0; + } + + null_value=0; for (ulong loop=0 ; loop < loop_count && !thd->killed; loop++) { - switch (args[0]->result_type()) { + switch (args[1]->result_type()) { case REAL_RESULT: - (void) args[0]->val_real(); + (void) args[1]->val_real(); break; case INT_RESULT: - (void) args[0]->val_int(); + (void) args[1]->val_int(); break; case STRING_RESULT: - (void) args[0]->val_str(&tmp); + (void) args[1]->val_str(&tmp); + break; + case DECIMAL_RESULT: + (void) args[1]->val_decimal(&tmp_decimal); break; case ROW_RESULT: default: @@ -3417,13 +3494,9 @@ longlong Item_func_benchmark::val_int() void Item_func_benchmark::print(String *str) { str->append(STRING_WITH_LEN("benchmark(")); - char buffer[20]; - // my_charset_bin is good enough for numbers - String st(buffer, sizeof(buffer), &my_charset_bin); - st.set((ulonglong)loop_count, &my_charset_bin); - str->append(st); - str->append(','); args[0]->print(str); + str->append(','); + args[1]->print(str); str->append(')'); } @@ -3621,8 +3694,9 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length, char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry)); if (entry->value == pos) entry->value=0; - if (!(entry->value=(char*) my_realloc(entry->value, length, - MYF(MY_ALLOW_ZERO_PTR)))) + entry->value= (char*) my_realloc(entry->value, length, + MYF(MY_ALLOW_ZERO_PTR | MY_WME)); + if (!entry->value) return 1; } } @@ -3644,7 +3718,8 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length, bool -Item_func_set_user_var::update_hash(void *ptr, uint length, Item_result type, +Item_func_set_user_var::update_hash(void *ptr, uint length, + Item_result res_type, CHARSET_INFO *cs, Derivation dv, bool unsigned_arg) { @@ -3653,9 +3728,9 @@ Item_func_set_user_var::update_hash(void *ptr, uint length, Item_result type, result type of the variable */ if ((null_value= args[0]->null_value) && null_item) - type= entry->type; // Don't change type of item + res_type= entry->type; // Don't change type of item if (::update_hash(entry, (null_value= args[0]->null_value), - ptr, length, type, cs, dv, unsigned_arg)) + ptr, length, res_type, cs, dv, unsigned_arg)) { current_thd->fatal_error(); // Probably end of memory null_value= 1; @@ -3801,8 +3876,7 @@ bool Item_func_set_user_var::check(bool use_result_field) { DBUG_ENTER("Item_func_set_user_var::check"); - if (use_result_field) - DBUG_ASSERT(result_field); + DBUG_ASSERT(!use_result_field || result_field); switch (cached_result_type) { case REAL_RESULT: @@ -3989,6 +4063,105 @@ void Item_func_set_user_var::make_field(Send_field *tmp_field) Item::make_field(tmp_field); } + +/* + Save the value of a user variable into a field + + SYNOPSIS + save_in_field() + field target field to save the value to + no_conversion flag indicating whether conversions are allowed + + DESCRIPTION + Save the function value into a field and update the user variable + accordingly. If a result field is defined and the target field doesn't + coincide with it then the value from the result field will be used as + the new value of the user variable. + + The reason to have this method rather than simply using the result + field in the val_xxx() methods is that the value from the result field + not always can be used when the result field is defined. + Let's consider the following cases: + 1) when filling a tmp table the result field is defined but the value of it + is undefined because it has to be produced yet. Thus we can't use it. + 2) on execution of an INSERT ... SELECT statement the save_in_field() + function will be called to fill the data in the new record. If the SELECT + part uses a tmp table then the result field is defined and should be + used in order to get the correct result. + + The difference between the SET_USER_VAR function and regular functions + like CONCAT is that the Item_func objects for the regular functions are + replaced by Item_field objects after the values of these functions have + been stored in a tmp table. Yet an object of the Item_field class cannot + be used to update a user variable. + Due to this we have to handle the result field in a special way here and + in the Item_func_set_user_var::send() function. + + RETURN VALUES + FALSE Ok + TRUE Error +*/ + +int Item_func_set_user_var::save_in_field(Field *field, bool no_conversions) +{ + bool use_result_field= (result_field && result_field != field); + int error; + + /* Update the value of the user variable */ + check(use_result_field); + update(); + + if (result_type() == STRING_RESULT || + result_type() == REAL_RESULT && + field->result_type() == STRING_RESULT) + { + String *result; + CHARSET_INFO *cs= collation.collation; + char buff[MAX_FIELD_WIDTH]; // Alloc buffer for small columns + str_value.set_quick(buff, sizeof(buff), cs); + result= entry->val_str(&null_value, &str_value, decimals); + + if (null_value) + { + str_value.set_quick(0, 0, cs); + return set_field_to_null_with_conversions(field, no_conversions); + } + + /* NOTE: If null_value == FALSE, "result" must be not NULL. */ + + field->set_notnull(); + error=field->store(result->ptr(),result->length(),cs); + str_value.set_quick(0, 0, cs); + } + else if (result_type() == REAL_RESULT) + { + double nr= entry->val_real(&null_value); + if (null_value) + return set_field_to_null(field); + field->set_notnull(); + error=field->store(nr); + } + else if (result_type() == DECIMAL_RESULT) + { + my_decimal decimal_value; + my_decimal *value= entry->val_decimal(&null_value, &decimal_value); + if (null_value) + return set_field_to_null(field); + field->set_notnull(); + error=field->store_decimal(value); + } + else + { + longlong nr= entry->val_int(&null_value); + if (null_value) + return set_field_to_null_with_conversions(field, no_conversions); + field->set_notnull(); + error=field->store(nr, unsigned_flag); + } + return error; +} + + String * Item_func_get_user_var::val_str(String *str) { @@ -4056,7 +4229,14 @@ int get_var_with_binlog(THD *thd, enum_sql_command sql_command, user_var_entry *var_entry; var_entry= get_variable(&thd->user_vars, name, 0); - if (!(opt_bin_log && is_update_query(sql_command))) + /* + Any reference to user-defined variable which is done from stored + function or trigger affects their execution and the execution of the + calling statement. We must log all such variables even if they are + not involved in table-updating statements. + */ + if (!(opt_bin_log && + (is_update_query(sql_command) || thd->in_sub_stmt))) { *out_entry= var_entry; return 0; @@ -4246,7 +4426,7 @@ bool Item_func_get_user_var::eq(const Item *item, bool binary_cmp) const bool Item_func_get_user_var::set_value(THD *thd, - sp_rcontext */*ctx*/, Item **it) + sp_rcontext * /*ctx*/, Item **it) { Item_func_set_user_var *suv= new Item_func_set_user_var(get_name(), *it); /* @@ -4695,7 +4875,7 @@ longlong Item_func_bit_xor::val_int() thd Thread handler var_type global / session name Name of base or system variable - component Component. + component Component NOTES If component.str = 0 then the variable name is in 'name' @@ -4807,8 +4987,9 @@ 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), +Item_func_sp::Item_func_sp(Name_resolution_context *context_arg, + sp_name *name_arg) + :Item_func(), context(context_arg), m_name(name_arg), m_sp(NULL), result_field(NULL) { maybe_null= 1; @@ -4819,8 +5000,8 @@ Item_func_sp::Item_func_sp(Name_resolution_context *context_arg, sp_name *name) Item_func_sp::Item_func_sp(Name_resolution_context *context_arg, - sp_name *name, List<Item> &list) - :Item_func(list), context(context_arg), m_name(name), m_sp(NULL), + sp_name *name_arg, List<Item> &list) + :Item_func(list), context(context_arg), m_name(name_arg), m_sp(NULL), result_field(NULL) { maybe_null= 1; @@ -4839,6 +5020,7 @@ Item_func_sp::cleanup() result_field= NULL; } m_sp= NULL; + dummy_table->alias= NULL; Item_func::cleanup(); } @@ -4975,6 +5157,18 @@ Item_func_sp::execute_impl(THD *thd, Field *return_value_fld) goto error; /* + Throw an error if a non-deterministic function is called while + statement-based replication (SBR) is active. + */ + if (!m_sp->m_chistics->detistic && !trust_function_creators && + (mysql_bin_log.is_open() && + thd->variables.binlog_format == BINLOG_FORMAT_STMT)) + { + my_error(ER_BINLOG_ROW_RBR_TO_SBR, MYF(0)); + goto error; + } + + /* Disable the binlogging if this is not a SELECT statement. If this is a SELECT, leave binlogging on, so execute_function() code writes the function call into binlog. @@ -5031,7 +5225,7 @@ Item_func_sp::result_type() const { Field *field; DBUG_ENTER("Item_func_sp::result_type"); - DBUG_PRINT("info", ("m_sp = %p", m_sp)); + DBUG_PRINT("info", ("m_sp: 0x%lx", (long) m_sp)); if (result_field) DBUG_RETURN(result_field->result_type()); diff --git a/sql/item_func.h b/sql/item_func.h index f820a89b5b6..3306b059097 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -157,7 +156,7 @@ public: return (null_value=args[0]->get_time(ltime)); } bool is_null() { - (void) val_int(); /* Discard result. It sets null_value as side-effect. */ + update_null_value(); return null_value; } void signal_divide_by_null(); @@ -241,7 +240,7 @@ public: virtual double real_op()= 0; virtual my_decimal *decimal_op(my_decimal *)= 0; virtual String *str_op(String *)= 0; - bool is_null() { (void) val_real(); return null_value; } + bool is_null() { update_null_value(); return null_value; } }; /* function where type of result detected by first argument */ @@ -434,6 +433,7 @@ public: my_decimal *decimal_op(my_decimal *); const char *func_name() const { return "%"; } void result_precision(); + void fix_length_and_dec(); }; @@ -679,6 +679,8 @@ public: bool const_item() const { return 0; } void update_used_tables(); bool fix_fields(THD *thd, Item **ref); +private: + void seed_random (Item * val); }; @@ -924,10 +926,9 @@ public: class Item_func_benchmark :public Item_int_func { - ulong loop_count; public: - Item_func_benchmark(ulong loop_count_arg,Item *expr) - :Item_int_func(expr), loop_count(loop_count_arg) + Item_func_benchmark(Item *count_expr, Item *expr) + :Item_int_func(count_expr, expr) {} longlong val_int(); const char *func_name() const { return "benchmark"; } @@ -979,6 +980,7 @@ public: Item_result result_type () const { return udf.result_type(); } table_map not_null_tables() const { return 0; } bool is_expensive() { return 1; } + void print(String *str); }; @@ -1214,6 +1216,7 @@ public: void print(String *str); void print_as_stmt(String *str); const char *func_name() const { return "set_user_var"; } + int save_in_field(Field *field, bool no_conversions); }; @@ -1314,10 +1317,10 @@ public: class Item_func_inet_aton : public Item_int_func { 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;} + 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;} bool check_partition_func_processor(byte *int_arg) {return FALSE;} }; diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index ef71dc9b8b5..78741483c0b 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -39,6 +38,7 @@ void Item_geometry_func::fix_length_and_dec() collation.set(&my_charset_bin); decimals=0; max_length=MAX_BLOB_WIDTH; + maybe_null= 1; } int Item_geometry_func::get_geometry_type() const @@ -67,11 +67,8 @@ String *Item_func_geometry_from_text::val_str(String *str) return 0; str->length(0); str->q_append(srid); - if (!Geometry::create_from_wkt(&buffer, &trs, str, 0)) - /* We shouldn't return NULL here as NULL is a legal spatial object */ - /* Geometry::bad_spatial_data will produce error message beeing stored*/ - /* in GEOMETRY field */ - return &Geometry::bad_geometry_data; + if ((null_value= !Geometry::create_from_wkt(&buffer, &trs, str, 0))) + return 0; return str; } @@ -125,6 +122,7 @@ String *Item_func_as_wkt::val_str(String *str) void Item_func_as_wkt::fix_length_and_dec() { max_length=MAX_BLOB_WIDTH; + maybe_null= 1; } @@ -390,7 +388,8 @@ String *Item_func_spatial_collection::val_str(String *str) for (i= 0; i < arg_count; ++i) { String *res= args[i]->val_str(&arg_value); - if (args[i]->null_value) + uint32 len; + if (args[i]->null_value || ((len= res->length()) < WKB_HEADER_SIZE)) goto err; if (coll_type == Geometry::wkb_geometrycollection) @@ -399,13 +398,12 @@ String *Item_func_spatial_collection::val_str(String *str) In the case of GeometryCollection we don't need any checkings for item types, so just copy them into target collection */ - if (str->append(res->ptr(), res->length(), (uint32) 512)) + if (str->append(res->ptr(), len, (uint32) 512)) goto err; } else { enum Geometry::wkbType wkb_type; - uint32 len=res->length(); const char *data= res->ptr() + 1; /* @@ -413,8 +411,6 @@ String *Item_func_spatial_collection::val_str(String *str) are of specific type, let's do this checking now */ - if (len < 5) - goto err; wkb_type= (Geometry::wkbType) uint4korr(data); data+= 4; len-= 5; @@ -536,9 +532,13 @@ longlong Item_func_spatial_rel::val_int() longlong Item_func_isempty::val_int() { DBUG_ASSERT(fixed == 1); - String tmp; - null_value=0; - return args[0]->null_value ? 1 : 0; + String tmp; + String *swkb= args[0]->val_str(&tmp); + Geometry_buffer buffer; + + null_value= args[0]->null_value || + !(Geometry::construct(&buffer, swkb->ptr(), swkb->length())); + return null_value ? 1 : 0; } @@ -546,10 +546,11 @@ longlong Item_func_issimple::val_int() { DBUG_ASSERT(fixed == 1); String tmp; - String *wkb=args[0]->val_str(&tmp); - - if ((null_value= (!wkb || args[0]->null_value))) - return 0; + String *swkb= args[0]->val_str(&tmp); + Geometry_buffer buffer; + + null_value= args[0]->null_value || + !(Geometry::construct(&buffer, swkb->ptr(), swkb->length())); /* TODO: Ramil or Holyfoot, add real IsSimple calculation */ return 0; } @@ -702,8 +703,9 @@ double Item_func_glength::val_real() null_value= (!swkb || !(geom= Geometry::construct(&buffer, - swkb->ptr(), swkb->length())) || - geom->length(&res)); + swkb->ptr(), + swkb->length())) || + geom->geom_length(&res)); return res; } diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h index 4848f59301d..068caa447ef 100644 --- a/sql/item_geofunc.h +++ b/sql/item_geofunc.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -35,6 +34,7 @@ public: enum_field_types field_type() const { return MYSQL_TYPE_GEOMETRY; } Field *tmp_table_field(TABLE *t_arg); virtual int get_geometry_type() const; + bool is_null() { (void) val_int(); return null_value; } }; class Item_func_geometry_from_text: public Item_geometry_func @@ -82,6 +82,7 @@ public: void fix_length_and_dec() { max_length=20; // "GeometryCollection" is the most long + maybe_null= 1; }; }; @@ -226,6 +227,8 @@ public: } } void print(String *str) { Item_func::print(str); } + void fix_length_and_dec() { maybe_null= 1; } + bool is_null() { (void) val_int(); return null_value; } }; class Item_func_isempty: public Item_bool_func @@ -235,6 +238,7 @@ public: longlong val_int(); optimize_type select_optimize() const { return OPTIMIZE_NONE; } const char *func_name() const { return "isempty"; } + void fix_length_and_dec() { maybe_null= 1; } }; class Item_func_issimple: public Item_bool_func @@ -244,6 +248,7 @@ public: longlong val_int(); optimize_type select_optimize() const { return OPTIMIZE_NONE; } const char *func_name() const { return "issimple"; } + void fix_length_and_dec() { maybe_null= 1; } }; class Item_func_isclosed: public Item_bool_func @@ -253,6 +258,7 @@ public: longlong val_int(); optimize_type select_optimize() const { return OPTIMIZE_NONE; } const char *func_name() const { return "isclosed"; } + void fix_length_and_dec() { maybe_null= 1; } }; class Item_func_dimension: public Item_int_func @@ -262,7 +268,7 @@ public: Item_func_dimension(Item *a): Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "dimension"; } - void fix_length_and_dec() { max_length=10; } + void fix_length_and_dec() { max_length= 10; maybe_null= 1; } }; class Item_func_x: public Item_real_func @@ -272,6 +278,11 @@ public: Item_func_x(Item *a): Item_real_func(a) {} double val_real(); const char *func_name() const { return "x"; } + void fix_length_and_dec() + { + Item_real_func::fix_length_and_dec(); + maybe_null= 1; + } }; @@ -282,6 +293,11 @@ public: Item_func_y(Item *a): Item_real_func(a) {} double val_real(); const char *func_name() const { return "y"; } + void fix_length_and_dec() + { + Item_real_func::fix_length_and_dec(); + maybe_null= 1; + } }; @@ -292,7 +308,7 @@ public: Item_func_numgeometries(Item *a): Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "numgeometries"; } - void fix_length_and_dec() { max_length=10; } + void fix_length_and_dec() { max_length= 10; maybe_null= 1; } }; @@ -303,7 +319,7 @@ public: Item_func_numinteriorring(Item *a): Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "numinteriorrings"; } - void fix_length_and_dec() { max_length=10; } + void fix_length_and_dec() { max_length= 10; maybe_null= 1; } }; @@ -314,7 +330,7 @@ public: Item_func_numpoints(Item *a): Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "numpoints"; } - void fix_length_and_dec() { max_length=10; } + void fix_length_and_dec() { max_length= 10; maybe_null= 1; } }; @@ -325,6 +341,11 @@ public: Item_func_area(Item *a): Item_real_func(a) {} double val_real(); const char *func_name() const { return "area"; } + void fix_length_and_dec() + { + Item_real_func::fix_length_and_dec(); + maybe_null= 1; + } }; @@ -335,6 +356,11 @@ public: Item_func_glength(Item *a): Item_real_func(a) {} double val_real(); const char *func_name() const { return "glength"; } + void fix_length_and_dec() + { + Item_real_func::fix_length_and_dec(); + maybe_null= 1; + } }; @@ -345,14 +371,14 @@ 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; } + void fix_length_and_dec() { max_length= 10; maybe_null= 1; } }; -#define GEOM_NEW(obj_constructor) new obj_constructor +#define GEOM_NEW(thd, obj_constructor) new (thd->mem_root) obj_constructor #else /*HAVE_SPATIAL*/ -#define GEOM_NEW(obj_constructor) NULL +#define GEOM_NEW(thd, obj_constructor) NULL #endif diff --git a/sql/item_row.cc b/sql/item_row.cc index 6e71ae0d4db..956556ca783 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/item_row.h b/sql/item_row.h index 39913086e8d..d55d3ae223f 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -72,7 +71,7 @@ public: Item *transform(Item_transformer transformer, byte *arg); uint cols() { return arg_count; } - Item* el(uint i) { return items[i]; } + Item* element_index(uint i) { return items[i]; } Item** addr(uint i) { return items + i; } bool check_cols(uint c); bool null_inside() { return with_null; }; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 7a82dd753b3..a011a2884c8 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -26,9 +25,6 @@ #include "mysql_priv.h" #include <m_ctype.h> -#ifdef HAVE_OPENSSL -#include <openssl/des.h> -#endif /* HAVE_OPENSSL */ #include "md5.h" #include "sha1.h" #include "my_aes.h" @@ -38,15 +34,6 @@ C_MODE_END String my_empty_string("",default_charset_info); -static void my_coll_agg_error(DTCollation &c1, DTCollation &c2, - const char *fname) -{ - my_error(ER_CANT_AGGREGATE_2COLLATIONS, MYF(0), - c1.collation->name, c1.derivation_name(), - c2.collation->name, c2.derivation_name(), - fname); -} - String *Item_str_func::check_well_formed_result(String *str) { @@ -80,6 +67,20 @@ String *Item_str_func::check_well_formed_result(String *str) } +bool Item_str_func::fix_fields(THD *thd, Item **ref) +{ + bool res= Item_func::fix_fields(thd, ref); + /* + In Item_str_func::check_well_formed_result() we may set null_value + flag on the same condition as in test() below. + */ + maybe_null= (maybe_null || + test(thd->variables.sql_mode & + (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES))); + return res; +} + + my_decimal *Item_str_func::val_decimal(my_decimal *decimal_value) { DBUG_ASSERT(fixed == 1); @@ -951,24 +952,35 @@ String *Item_func_insert::val_str(String *str) { DBUG_ASSERT(fixed == 1); String *res,*res2; - uint start,length; + longlong start, length; /* must be longlong to avoid truncation */ null_value=0; res=args[0]->val_str(str); res2=args[3]->val_str(&tmp_value); - start=(uint) args[1]->val_int()-1; - length=(uint) args[2]->val_int(); + start= args[1]->val_int() - 1; + length= args[2]->val_int(); + if (args[0]->null_value || args[1]->null_value || args[2]->null_value || args[3]->null_value) goto null; /* purecov: inspected */ - start=res->charpos(start); - length=res->charpos(length,start); - if (start > res->length()+1) - return res; // Wrong param; skip insert - if (length > res->length()-start) - length=res->length()-start; - if (res->length() - length + res2->length() > - current_thd->variables.max_allowed_packet) + + if ((start < 0) || (start > res->length())) + return res; // Wrong param; skip insert + if ((length < 0) || (length > res->length())) + length= res->length(); + + /* start and length are now sufficiently valid to pass to charpos function */ + start= res->charpos((int) start); + length= res->charpos((int) length, (uint32) start); + + /* Re-testing with corrected params */ + if (start > res->length()) + return res; /* purecov: inspected */ // Wrong param; skip insert + if (length > res->length() - start) + length= res->length() - start; + + if ((ulonglong) (res->length() - length + res2->length()) > + (ulonglong) current_thd->variables.max_allowed_packet) { push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, @@ -977,7 +989,7 @@ String *Item_func_insert::val_str(String *str) goto null; } res=copy_if_not_alloced(str,res,res->length()); - res->replace(start,length,*res2); + res->replace((uint32) start,(uint32) length,*res2); return res; null: null_value=1; @@ -1039,16 +1051,21 @@ String *Item_str_conv::val_str(String *str) String *Item_func_left::val_str(String *str) { DBUG_ASSERT(fixed == 1); - String *res =args[0]->val_str(str); - long length =(long) args[1]->val_int(); + String *res= args[0]->val_str(str); + + /* must be longlong to avoid truncation */ + longlong length= args[1]->val_int(); uint char_pos; if ((null_value=(args[0]->null_value || args[1]->null_value))) return 0; - if (length <= 0) + + /* if "unsigned_flag" is set, we have a *huge* positive number. */ + if ((length <= 0) && (!args[1]->unsigned_flag)) return &my_empty_string; - if (res->length() <= (uint) length || - res->length() <= (char_pos= res->charpos(length))) + + if ((res->length() <= (ulonglong) length) || + (res->length() <= (char_pos= res->charpos((int) length)))) return res; tmp_value.set(*res, 0, char_pos); @@ -1080,14 +1097,18 @@ void Item_func_left::fix_length_and_dec() String *Item_func_right::val_str(String *str) { DBUG_ASSERT(fixed == 1); - String *res =args[0]->val_str(str); - long length =(long) args[1]->val_int(); + String *res= args[0]->val_str(str); + /* must be longlong to avoid truncation */ + longlong length= args[1]->val_int(); if ((null_value=(args[0]->null_value || args[1]->null_value))) return 0; /* purecov: inspected */ - if (length <= 0) + + /* if "unsigned_flag" is set, we have a *huge* positive number. */ + if ((length <= 0) && (!args[1]->unsigned_flag)) return &my_empty_string; /* purecov: inspected */ - if (res->length() <= (uint) length) + + if (res->length() <= (ulonglong) length) return res; /* purecov: inspected */ uint start=res->numchars(); @@ -1110,25 +1131,44 @@ String *Item_func_substr::val_str(String *str) { DBUG_ASSERT(fixed == 1); String *res = args[0]->val_str(str); - int32 start = (int32) args[1]->val_int(); - int32 length = arg_count == 3 ? (int32) args[2]->val_int() : INT_MAX32; - int32 tmp_length; + /* must be longlong to avoid truncation */ + longlong start= args[1]->val_int(); + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Limit so that code sees out-of-bound value properly. */ + longlong length= arg_count == 3 ? args[2]->val_int() : INT_MAX32; + longlong tmp_length; if ((null_value=(args[0]->null_value || args[1]->null_value || (arg_count == 3 && args[2]->null_value)))) return 0; /* purecov: inspected */ - start= (int32)((start < 0) ? res->numchars() + start : start -1); - start=res->charpos(start); - length=res->charpos(length,start); - if (start < 0 || (uint) start+1 > res->length() || length <= 0) + + /* Negative length, will return empty string. */ + if ((arg_count == 3) && (length <= 0) && !args[2]->unsigned_flag) + return &my_empty_string; + + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Set here so that rest of code sees out-of-bound value as such. */ + if ((length <= 0) || (length > INT_MAX32)) + length= INT_MAX32; + + /* if "unsigned_flag" is set, we have a *huge* positive number. */ + /* Assumes that the maximum length of a String is < INT_MAX32. */ + if ((!args[1]->unsigned_flag && (start < INT_MIN32 || start > INT_MAX32)) || + (args[1]->unsigned_flag && ((ulonglong) start > INT_MAX32))) + return &my_empty_string; + + start= ((start < 0) ? res->numchars() + start : start - 1); + start= res->charpos((int) start); + if ((start < 0) || ((uint) start + 1 > res->length())) return &my_empty_string; - tmp_length=(int32) res->length()-start; - length=min(length,tmp_length); + length= res->charpos((int) length, (uint32) start); + tmp_length= res->length() - start; + length= min(length, tmp_length); - if (!start && res->length() == (uint) length) + if (!start && (longlong) res->length() == length) return res; - tmp_value.set(*res,(uint) start,(uint) length); + tmp_value.set(*res, (uint32) start, (uint32) length); return &tmp_value; } @@ -1141,11 +1181,10 @@ void Item_func_substr::fix_length_and_dec() if (args[1]->const_item()) { int32 start= (int32) args[1]->val_int(); - start= (int32)((start < 0) ? max_length + start : start - 1); - if (start < 0 || start >= (int32) max_length) - max_length=0; /* purecov: inspected */ + if (start < 0) + max_length= ((uint)(-start) > max_length) ? 0 : (uint)(-start); else - max_length-= (uint) start; + max_length-= min((uint)(start - 1), max_length); } if (arg_count == 3 && args[2]->const_item()) { @@ -1621,21 +1660,33 @@ String *Item_func_encrypt::val_str(String *str) void Item_func_encode::fix_length_and_dec() { max_length=args[0]->max_length; - maybe_null=args[0]->maybe_null; + maybe_null=args[0]->maybe_null || args[1]->maybe_null; collation.set(&my_charset_bin); } String *Item_func_encode::val_str(String *str) { - DBUG_ASSERT(fixed == 1); String *res; + char pw_buff[80]; + String tmp_pw_value(pw_buff, sizeof(pw_buff), system_charset_info); + String *password; + DBUG_ASSERT(fixed == 1); + if (!(res=args[0]->val_str(str))) { null_value=1; /* purecov: inspected */ return 0; /* purecov: inspected */ } + + if (!(password=args[1]->val_str(& tmp_pw_value))) + { + null_value=1; + return 0; + } + null_value=0; res=copy_if_not_alloced(str,res,res->length()); + SQL_CRYPT sql_crypt(password->ptr()); sql_crypt.init(); sql_crypt.encode((char*) res->ptr(),res->length()); res->set_charset(&my_charset_bin); @@ -1644,15 +1695,27 @@ String *Item_func_encode::val_str(String *str) String *Item_func_decode::val_str(String *str) { - DBUG_ASSERT(fixed == 1); String *res; + char pw_buff[80]; + String tmp_pw_value(pw_buff, sizeof(pw_buff), system_charset_info); + String *password; + DBUG_ASSERT(fixed == 1); + if (!(res=args[0]->val_str(str))) { null_value=1; /* purecov: inspected */ return 0; /* purecov: inspected */ } + + if (!(password=args[1]->val_str(& tmp_pw_value))) + { + null_value=1; + return 0; + } + null_value=0; res=copy_if_not_alloced(str,res,res->length()); + SQL_CRYPT sql_crypt(password->ptr()); sql_crypt.init(); sql_crypt.decode((char*) res->ptr(),res->length()); return res; @@ -1735,8 +1798,13 @@ bool Item_func_current_user::fix_fields(THD *thd, Item **ref) if (Item_func_sysconst::fix_fields(thd, ref)) return TRUE; - Security_context *ctx= (context->security_ctx + Security_context *ctx= +#ifndef NO_EMBEDDED_ACCESS_CHECKS + (context->security_ctx ? context->security_ctx : thd->security_ctx); +#else + thd->security_ctx; +#endif /*NO_EMBEDDED_ACCESS_CHECKS*/ return init(ctx->priv_user, ctx->priv_host); } @@ -1822,9 +1890,19 @@ String *Item_func_soundex::val_str(String *str) ** This should be 'internationalized' sometimes. */ -Item_func_format::Item_func_format(Item *org,int dec) :Item_str_func(org) +const int FORMAT_MAX_DECIMALS= 30; + +Item_func_format::Item_func_format(Item *org, Item *dec) +: Item_str_func(org, dec) { - decimals=(uint) set_zone(dec,0,30); +} + +void Item_func_format::fix_length_and_dec() +{ + collation.set(default_charset()); + uint char_length= args[0]->max_length/args[0]->collation.collation->mbmaxlen; + max_length= ((char_length + (char_length-args[0]->decimals)/3) * + collation.collation->mbmaxlen); } @@ -1835,10 +1913,25 @@ Item_func_format::Item_func_format(Item *org,int dec) :Item_str_func(org) String *Item_func_format::val_str(String *str) { - uint32 length, str_length ,dec; + uint32 length; + uint32 str_length; + /* Number of decimal digits */ + int dec; + /* Number of characters used to represent the decimals, including '.' */ + uint32 dec_length; int diff; DBUG_ASSERT(fixed == 1); - dec= decimals ? decimals+1 : 0; + + dec= (int) args[1]->val_int(); + if (args[1]->null_value) + { + null_value=1; + return NULL; + } + + dec= set_zone(dec, 0, FORMAT_MAX_DECIMALS); + dec_length= dec ? dec+1 : 0; + null_value=0; if (args[0]->result_type() == DECIMAL_RESULT || args[0]->result_type() == INT_RESULT) @@ -1847,7 +1940,7 @@ String *Item_func_format::val_str(String *str) res= args[0]->val_decimal(&dec_val); if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ - my_decimal_round(E_DEC_FATAL_ERROR, res, decimals, false, &rnd_dec); + my_decimal_round(E_DEC_FATAL_ERROR, res, dec, false, &rnd_dec); my_decimal2string(E_DEC_FATAL_ERROR, &rnd_dec, 0, 0, 0, str); str_length= str->length(); if (rnd_dec.sign()) @@ -1858,9 +1951,9 @@ String *Item_func_format::val_str(String *str) double nr= args[0]->val_real(); if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ - nr= my_double_round(nr, decimals, FALSE); + nr= my_double_round(nr, dec, FALSE); /* Here default_charset() is right as this is not an automatic conversion */ - str->set_real(nr,decimals, default_charset()); + str->set_real(nr, dec, default_charset()); if (isnan(nr)) return str; str_length=str->length(); @@ -1868,13 +1961,13 @@ String *Item_func_format::val_str(String *str) str_length--; // Don't count sign } /* We need this test to handle 'nan' values */ - if (str_length >= dec+4) + if (str_length >= dec_length+4) { char *tmp,*pos; - length= str->length()+(diff=((int)(str_length- dec-1))/3); + length= str->length()+(diff=((int)(str_length- dec_length-1))/3); str= copy_if_not_alloced(&tmp_str,str,length); str->length(length); - tmp= (char*) str->ptr()+length - dec-1; + tmp= (char*) str->ptr()+length - dec_length-1; for (pos= (char*) str->ptr()+length-1; pos != tmp; pos--) pos[0]= pos[-diff]; while (diff) @@ -1898,12 +1991,8 @@ void Item_func_format::print(String *str) { str->append(STRING_WITH_LEN("format(")); args[0]->print(str); - str->append(','); - // my_charset_bin is good enough for numbers - char buffer[20]; - String st(buffer, sizeof(buffer), &my_charset_bin); - st.set((ulonglong)decimals, &my_charset_bin); - str->append(st); + str->append(','); + args[1]->print(str); str->append(')'); } @@ -2141,8 +2230,15 @@ void Item_func_repeat::fix_length_and_dec() collation.set(args[0]->collation); if (args[1]->const_item()) { - ulonglong max_result_length= ((ulonglong) args[0]->max_length * - args[1]->val_int()); + /* must be longlong to avoid truncation */ + longlong count= args[1]->val_int(); + + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Set here so that rest of code sees out-of-bound value as such. */ + if (count > INT_MAX32) + count= INT_MAX32; + + ulonglong max_result_length= (ulonglong) args[0]->max_length * count; if (max_result_length >= MAX_BLOB_WIDTH) { max_result_length= MAX_BLOB_WIDTH; @@ -2167,19 +2263,26 @@ String *Item_func_repeat::val_str(String *str) DBUG_ASSERT(fixed == 1); uint length,tot_length; char *to; - long count= (long) args[1]->val_int(); - String *res =args[0]->val_str(str); + /* must be longlong to avoid truncation */ + longlong count= args[1]->val_int(); + String *res= args[0]->val_str(str); if (args[0]->null_value || args[1]->null_value) goto err; // string and/or delim are null - null_value=0; - if (count <= 0) // For nicer SQL code + null_value= 0; + + if (count == 0 || count < 0 && !args[1]->unsigned_flag) return &my_empty_string; + + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Bounds check on count: If this is triggered, we will error. */ + if ((ulonglong) count > INT_MAX32) + count= INT_MAX32; if (count == 1) // To avoid reallocs return res; length=res->length(); // Safe length check - if (length > current_thd->variables.max_allowed_packet/count) + if (length > current_thd->variables.max_allowed_packet / (uint) count) { push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, @@ -2212,8 +2315,20 @@ void Item_func_rpad::fix_length_and_dec() return; if (args[1]->const_item()) { - ulonglong length= ((ulonglong) args[1]->val_int() * - collation.collation->mbmaxlen); + ulonglong length= 0; + + if (collation.collation->mbmaxlen > 0) + { + ulonglong temp= (ulonglong) args[1]->val_int(); + + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Set here so that rest of code sees out-of-bound value as such. */ + if (temp > INT_MAX32) + temp = INT_MAX32; + + length= temp * collation.collation->mbmaxlen; + } + if (length >= MAX_BLOB_WIDTH) { length= MAX_BLOB_WIDTH; @@ -2235,21 +2350,29 @@ String *Item_func_rpad::val_str(String *str) uint32 res_byte_length,res_char_length,pad_char_length,pad_byte_length; char *to; const char *ptr_pad; - int32 count= (int32) args[1]->val_int(); - int32 byte_count= count * collation.collation->mbmaxlen; - String *res =args[0]->val_str(str); - String *rpad = args[2]->val_str(&rpad_str); + /* must be longlong to avoid truncation */ + longlong count= args[1]->val_int(); + longlong byte_count; + String *res= args[0]->val_str(str); + String *rpad= args[2]->val_str(&rpad_str); - if (!res || args[1]->null_value || !rpad || count < 0) + if (!res || args[1]->null_value || !rpad || + ((count < 0) && !args[1]->unsigned_flag)) goto err; null_value=0; - if (count <= (int32) (res_char_length=res->numchars())) + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Set here so that rest of code sees out-of-bound value as such. */ + if ((ulonglong) count > INT_MAX32) + count= INT_MAX32; + if (count <= (res_char_length= res->numchars())) { // String to pad is big enough - res->length(res->charpos(count)); // Shorten result if longer + res->length(res->charpos((int) count)); // Shorten result if longer return (res); } pad_char_length= rpad->numchars(); - if ((ulong) byte_count > current_thd->variables.max_allowed_packet) + + byte_count= count * collation.collation->mbmaxlen; + if ((ulonglong) byte_count > current_thd->variables.max_allowed_packet) { push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, @@ -2260,7 +2383,7 @@ String *Item_func_rpad::val_str(String *str) if (args[2]->null_value || !pad_char_length) goto err; res_byte_length= res->length(); /* Must be done before alloc_buffer */ - if (!(res= alloc_buffer(res,str,&tmp_value,byte_count))) + if (!(res= alloc_buffer(res,str,&tmp_value, (ulong) byte_count))) goto err; to= (char*) res->ptr()+res_byte_length; @@ -2274,7 +2397,7 @@ String *Item_func_rpad::val_str(String *str) } if (count) { - pad_byte_length= rpad->charpos(count); + pad_byte_length= rpad->charpos((int) count); memcpy(to,ptr_pad,(size_t) pad_byte_length); to+= pad_byte_length; } @@ -2295,8 +2418,20 @@ void Item_func_lpad::fix_length_and_dec() if (args[1]->const_item()) { - ulonglong length= ((ulonglong) args[1]->val_int() * - collation.collation->mbmaxlen); + ulonglong length= 0; + + if (collation.collation->mbmaxlen > 0) + { + ulonglong temp= (ulonglong) args[1]->val_int(); + + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Set here so that rest of code sees out-of-bound value as such. */ + if (temp > INT_MAX32) + temp= INT_MAX32; + + length= temp * collation.collation->mbmaxlen; + } + if (length >= MAX_BLOB_WIDTH) { length= MAX_BLOB_WIDTH; @@ -2316,26 +2451,33 @@ String *Item_func_lpad::val_str(String *str) { DBUG_ASSERT(fixed == 1); uint32 res_char_length,pad_char_length; - ulong count= (long) args[1]->val_int(), byte_count; + /* must be longlong to avoid truncation */ + longlong count= args[1]->val_int(); + longlong byte_count; String *res= args[0]->val_str(&tmp_value); String *pad= args[2]->val_str(&lpad_str); - if (!res || args[1]->null_value || !pad) - goto err; - + if (!res || args[1]->null_value || !pad || + ((count < 0) && !args[1]->unsigned_flag)) + goto err; null_value=0; + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Set here so that rest of code sees out-of-bound value as such. */ + if ((ulonglong) count > INT_MAX32) + count= INT_MAX32; + res_char_length= res->numchars(); if (count <= res_char_length) { - res->length(res->charpos(count)); + res->length(res->charpos((int) count)); return res; } pad_char_length= pad->numchars(); byte_count= count * collation.collation->mbmaxlen; - if (byte_count > current_thd->variables.max_allowed_packet) + if ((ulonglong) byte_count > current_thd->variables.max_allowed_packet) { push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, @@ -2344,7 +2486,8 @@ String *Item_func_lpad::val_str(String *str) goto err; } - if (args[2]->null_value || !pad_char_length || str->alloc(byte_count)) + if (args[2]->null_value || !pad_char_length || + str->alloc((uint32) byte_count)) goto err; str->length(0); @@ -2356,7 +2499,7 @@ String *Item_func_lpad::val_str(String *str) count-= pad_char_length; } if (count > 0) - str->append(pad->ptr(), pad->charpos(count), collation.collation); + str->append(pad->ptr(), pad->charpos((int) count), collation.collation); str->append(*res); null_value= 0; @@ -2658,6 +2801,11 @@ String *Item_load_file::val_str(String *str) (void) fn_format(path, file_name->c_ptr(), mysql_real_data_home, "", MY_RELATIVE_PATH | MY_UNPACK_FILENAME); + /* Read only allowed from within dir specified by secure_file_priv */ + if (opt_secure_file_priv && + strncmp(opt_secure_file_priv, path, strlen(opt_secure_file_priv))) + goto err; + if (!my_stat(path, &stat_info, MYF(0))) goto err; @@ -2965,6 +3113,7 @@ String *Item_func_compress::val_str(String *str) null_value= 1; return 0; } + null_value= 0; if (res->is_empty()) return res; /* @@ -3118,15 +3267,17 @@ String *Item_func_uuid::val_str(String *str) int i; if (my_gethwaddr(mac)) { + /* purecov: begin inspected */ /* generating random "hardware addr" and because specs explicitly specify that it should NOT correlate with a clock_seq value (initialized random below), we use a separate randominit() here */ - randominit(&uuid_rand, tmp + (ulong) thd, tmp + (ulong)query_id); + randominit(&uuid_rand, tmp + (ulong) thd, tmp + (ulong)global_query_id); for (i=0; i < (int)sizeof(mac); i++) mac[i]=(uchar)(my_rnd(&uuid_rand)*255); + /* purecov: end */ } s=clock_seq_and_node_str+sizeof(clock_seq_and_node_str)-1; for (i=sizeof(mac)-1 ; i>=0 ; i--) @@ -3134,7 +3285,7 @@ String *Item_func_uuid::val_str(String *str) *--s=_dig_vec_lower[mac[i] & 15]; *--s=_dig_vec_lower[mac[i] >> 4]; } - randominit(&uuid_rand, tmp + (ulong)start_time, + randominit(&uuid_rand, tmp + (ulong) server_start_time, tmp + thd->status_var.bytes_sent); set_clock_seq_str(); } @@ -3175,4 +3326,3 @@ String *Item_func_uuid::val_str(String *str) strmov(s+18, clock_seq_and_node_str); return str; } - diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 2adccd212b3..628b51a70d7 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -37,6 +36,7 @@ public: enum Item_result result_type () const { return STRING_RESULT; } void left_right_max_length(); String *check_well_formed_result(String *str); + bool fix_fields(THD *thd, Item **ref); }; class Item_func_md5 :public Item_str_func @@ -360,11 +360,9 @@ public: class Item_func_encode :public Item_str_func { - protected: - SQL_CRYPT sql_crypt; public: - Item_func_encode(Item *a, char *seed): - Item_str_func(a),sql_crypt(seed) {} + Item_func_encode(Item *a, Item *seed): + Item_str_func(a, seed) {} String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "encode"; } @@ -374,7 +372,7 @@ public: class Item_func_decode :public Item_func_encode { public: - Item_func_decode(Item *a, char *seed): Item_func_encode(a,seed) {} + Item_func_decode(Item *a, Item *seed): Item_func_encode(a, seed) {} String *val_str(String *); const char *func_name() const { return "decode"; } }; @@ -507,15 +505,9 @@ class Item_func_format :public Item_str_func { String tmp_str; public: - Item_func_format(Item *org,int dec); + Item_func_format(Item *org, Item *dec); String *val_str(String *); - void fix_length_and_dec() - { - collation.set(default_charset()); - uint char_length= args[0]->max_length/args[0]->collation.collation->mbmaxlen; - max_length= ((char_length + (char_length-args[0]->decimals)/3) * - collation.collation->mbmaxlen); - } + void fix_length_and_dec(); const char *func_name() const { return "format"; } void print(String *); }; @@ -530,9 +522,8 @@ public: { collation.set(cs); } String *val_str(String *); void fix_length_and_dec() - { - maybe_null=0; - max_length=arg_count * collation.collation->mbmaxlen; + { + max_length= arg_count * collation.collation->mbmaxlen; } const char *func_name() const { return "char"; } }; @@ -610,7 +601,11 @@ class Item_func_unhex :public Item_str_func { String tmp_value; public: - Item_func_unhex(Item *a) :Item_str_func(a) {} + Item_func_unhex(Item *a) :Item_str_func(a) + { + /* there can be bad hex strings */ + maybe_null= 1; + } const char *func_name() const { return "unhex"; } String *val_str(String *); void fix_length_and_dec() diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 4e6c67cba60..135ed33feec 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -37,7 +36,7 @@ inline Item * and_items(Item* cond, Item *item) Item_subselect::Item_subselect(): Item_result_field(), value_assigned(0), thd(0), substitution(0), engine(0), old_engine(0), used_tables_cache(0), have_to_be_excluded(0), - const_item_cache(1), engine_changed(0), changed(0) + const_item_cache(1), engine_changed(0), changed(0), is_correlated(FALSE) { with_subselect= 1; reset(); @@ -52,9 +51,13 @@ Item_subselect::Item_subselect(): void Item_subselect::init(st_select_lex *select_lex, select_subselect *result) { + /* + Please see Item_singlerow_subselect::invalidate_and_restore_select_lex(), + which depends on alterations to the parse tree implemented here. + */ DBUG_ENTER("Item_subselect::init"); - DBUG_PRINT("enter", ("select_lex: 0x%x", (ulong) select_lex)); + DBUG_PRINT("enter", ("select_lex: 0x%lx", (long) select_lex)); unit= select_lex->master_unit(); if (unit->item) @@ -92,6 +95,12 @@ void Item_subselect::init(st_select_lex *select_lex, DBUG_VOID_RETURN; } +st_select_lex * +Item_subselect::get_select_lex() +{ + return unit->first_select(); +} + void Item_subselect::cleanup() { DBUG_ENTER("Item_subselect::cleanup"); @@ -239,6 +248,10 @@ bool Item_subselect::exec() { int res; + if (thd->net.report_error) + /* Do not execute subselect in case of a fatal error */ + return 1; + res= engine->exec(); if (engine_changed) @@ -272,11 +285,11 @@ bool Item_subselect::const_item() const return const_item_cache; } -Item *Item_subselect::get_tmp_table_item(THD *thd) +Item *Item_subselect::get_tmp_table_item(THD *thd_arg) { if (!with_sum_func && !const_item()) return new Item_field(result_field); - return copy_or_same(thd); + return copy_or_same(thd_arg); } void Item_subselect::update_used_tables() @@ -308,6 +321,26 @@ Item_singlerow_subselect::Item_singlerow_subselect(st_select_lex *select_lex) DBUG_VOID_RETURN; } +st_select_lex * +Item_singlerow_subselect::invalidate_and_restore_select_lex() +{ + DBUG_ENTER("Item_singlerow_subselect::invalidate_and_restore_select_lex"); + st_select_lex *result= get_select_lex(); + + DBUG_ASSERT(result); + + /* + This code restore the parse tree in it's state before the execution of + Item_singlerow_subselect::Item_singlerow_subselect(), + and in particular decouples this object from the SELECT_LEX, + so that the SELECT_LEX can be used with a different flavor + or Item_subselect instead, as part of query rewriting. + */ + unit->item= NULL; + + DBUG_RETURN(result); +} + Item_maxmin_subselect::Item_maxmin_subselect(THD *thd_param, Item_subselect *parent, st_select_lex *select_lex, @@ -392,6 +425,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join) */ !(select_lex->item_list.head()->type() == FIELD_ITEM || select_lex->item_list.head()->type() == REF_ITEM) && + !join->conds && !join->having && /* switch off this optimization for prepare statement, because we do not rollback this changes @@ -416,8 +450,6 @@ Item_singlerow_subselect::select_transformer(JOIN *join) */ substitution->walk(&Item::remove_dependence_processor, 0, (byte *) select_lex->outer_select()); - /* SELECT without FROM clause can't have WHERE or HAVING clause */ - DBUG_ASSERT(join->conds == 0 && join->having == 0); return RES_REDUCE; } return RES_OK; @@ -434,6 +466,15 @@ enum Item_result Item_singlerow_subselect::result_type() const return engine->type(); } +/* + Don't rely on the result type to calculate field type. + Ask the engine instead. +*/ +enum_field_types Item_singlerow_subselect::field_type() const +{ + return engine->field_type(); +} + void Item_singlerow_subselect::fix_length_and_dec() { if ((max_columns= engine->cols()) == 1) @@ -583,13 +624,13 @@ void Item_exists_subselect::print(String *str) } -bool Item_in_subselect::test_limit(SELECT_LEX_UNIT *unit) +bool Item_in_subselect::test_limit(SELECT_LEX_UNIT *unit_arg) { - if (unit->fake_select_lex && - unit->fake_select_lex->test_limit()) + if (unit_arg->fake_select_lex && + unit_arg->fake_select_lex->test_limit()) return(1); - SELECT_LEX *sl= unit->first_select(); + SELECT_LEX *sl= unit_arg->first_select(); for (; sl; sl= sl->next_select()) { if (sl->test_limit()) @@ -600,7 +641,8 @@ bool Item_in_subselect::test_limit(SELECT_LEX_UNIT *unit) Item_in_subselect::Item_in_subselect(Item * left_exp, st_select_lex *select_lex): - Item_exists_subselect(), optimizer(0), transformed(0), upper_item(0) + Item_exists_subselect(), optimizer(0), transformed(0), + pushed_cond_guards(NULL), upper_item(0) { DBUG_ENTER("Item_in_subselect::Item_in_subselect"); left_expr= left_exp; @@ -710,6 +752,7 @@ double Item_in_subselect::val_real() */ DBUG_ASSERT(0); DBUG_ASSERT(fixed == 1); + null_value= 0; if (exec()) { reset(); @@ -730,6 +773,7 @@ longlong Item_in_subselect::val_int() */ DBUG_ASSERT(0); DBUG_ASSERT(fixed == 1); + null_value= 0; if (exec()) { reset(); @@ -750,6 +794,7 @@ String *Item_in_subselect::val_str(String *str) */ DBUG_ASSERT(0); DBUG_ASSERT(fixed == 1); + null_value= 0; if (exec()) { reset(); @@ -769,6 +814,7 @@ String *Item_in_subselect::val_str(String *str) bool Item_in_subselect::val_bool() { DBUG_ASSERT(fixed == 1); + null_value= 0; if (exec()) { reset(); @@ -787,6 +833,7 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value) method should not be used */ DBUG_ASSERT(0); + null_value= 0; DBUG_ASSERT(fixed == 1); if (exec()) { @@ -801,13 +848,60 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value) } -/* Rewrite a single-column IN/ALL/ANY subselect. */ +/* + Rewrite a single-column IN/ALL/ANY subselect + + SYNOPSIS + Item_in_subselect::single_value_transformer() + join Join object of the subquery (i.e. 'child' join). + func Subquery comparison creator + + DESCRIPTION + Rewrite a single-column subquery using rule-based approach. The subquery + + oe $cmp$ (SELECT ie FROM ... WHERE subq_where ... HAVING subq_having) + + First, try to convert the subquery to scalar-result subquery in one of + the forms: + + - oe $cmp$ (SELECT MAX(...) ) // handled by Item_singlerow_subselect + - oe $cmp$ <max>(SELECT ...) // handled by Item_maxmin_subselect + + If that fails, the subquery will be handled with class Item_in_optimizer, + Inject the predicates into subquery, i.e. convert it to: + + - If the subquery has aggregates, GROUP BY, or HAVING, convert to + + SELECT ie FROM ... HAVING subq_having AND + trigcond(oe $cmp$ ref_or_null_helper<ie>) + + the addition is wrapped into trigger only when we want to distinguish + between NULL and FALSE results. + + - Otherwise (no aggregates/GROUP BY/HAVING) convert it to one of the + following: + + = If we don't need to distinguish between NULL and FALSE subquery: + + SELECT 1 FROM ... WHERE (oe $cmp$ ie) AND subq_where + + = If we need to distinguish between those: + + SELECT 1 FROM ... + WHERE subq_where AND trigcond((oe $cmp$ ie) OR (ie IS NULL)) + HAVING trigcond(<is_not_null_test>(ie)) + + RETURN + RES_OK - OK, either subquery was transformed, or appopriate + predicates where injected into it. + RES_REDUCE - The subquery was reduced to non-subquery + RES_ERROR - Error +*/ Item_subselect::trans_res Item_in_subselect::single_value_transformer(JOIN *join, Comp_creator *func) { - Item_subselect::trans_res result= RES_ERROR; SELECT_LEX *select_lex= join->select_lex; DBUG_ENTER("Item_in_subselect::single_value_transformer"); @@ -904,8 +998,8 @@ Item_in_subselect::single_value_transformer(JOIN *join, if (!substitution) { - //first call for this unit - SELECT_LEX_UNIT *unit= select_lex->master_unit(); + /* We're invoked for the 1st (or the only) SELECT in the subquery UNION */ + SELECT_LEX_UNIT *master_unit= select_lex->master_unit(); substitution= optimizer; SELECT_LEX *current= thd->lex->current_select, *up; @@ -928,17 +1022,16 @@ Item_in_subselect::single_value_transformer(JOIN *join, (char *)"<no matter>", (char *)in_left_expr_name); - unit->uncacheable|= UNCACHEABLE_DEPENDENT; + master_unit->uncacheable|= UNCACHEABLE_DEPENDENT; + } + if (!abort_on_null && left_expr->maybe_null && !pushed_cond_guards) + { + if (!(pushed_cond_guards= (bool*)join->thd->alloc(sizeof(bool)))) + DBUG_RETURN(RES_ERROR); + pushed_cond_guards[0]= TRUE; } select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; - /* - Add the left part of a subselect to a WHERE or HAVING clause of - the right part, e.g. SELECT 1 IN (SELECT a FROM t1) => - SELECT Item_in_optimizer(1, SELECT a FROM t1 WHERE a=1) - HAVING is used only if the right part contains a SUM function, a GROUP - BY or a HAVING clause. - */ if (join->having || select_lex->with_sum_func || select_lex->group_list.elements) { @@ -950,16 +1043,23 @@ Item_in_subselect::single_value_transformer(JOIN *join, ref_pointer_array, (char *)"<ref>", this->full_name())); -#ifdef CORRECT_BUT_TOO_SLOW_TO_BE_USABLE if (!abort_on_null && left_expr->maybe_null) - item= new Item_cond_or(new Item_func_isnull(left_expr), item); -#endif + { + /* + We can encounter "NULL IN (SELECT ...)". Wrap the added condition + within a trig_cond. + */ + item= new Item_func_trig_cond(item, get_cond_guard(0)); + } + /* AND and comparison functions can't be changed during fix_fields() we can assign select_lex->having here, and pass 0 as last argument (reference) to fix_fields() */ select_lex->having= join->having= and_items(join->having, item); + if (join->having == item) + item->name= (char*)in_having_cond; select_lex->having_fix_field= 1; /* we do not check join->having->fixed, because Item_and (from and_items) @@ -982,19 +1082,24 @@ Item_in_subselect::single_value_transformer(JOIN *join, select_lex->item_list.push_back(new Item_int("Not_used", (longlong) 1, 21)); select_lex->ref_pointer_array[0]= select_lex->item_list.head(); + item= func->create(expr, item); if (!abort_on_null && orig_item->maybe_null) { having= new Item_is_not_null_test(this, having); + if (left_expr->maybe_null) + { + if (!(having= new Item_func_trig_cond(having, + get_cond_guard(0)))) + DBUG_RETURN(RES_ERROR); + } /* Item_is_not_null_test can't be changed during fix_fields() we can assign select_lex->having here, and pass 0 as last argument (reference) to fix_fields() */ - select_lex->having= - join->having= (join->having ? - new Item_cond_and(having, join->having) : - having); + having->name= (char*)in_having_cond; + select_lex->having= join->having= having; select_lex->having_fix_field= 1; /* we do not check join->having->fixed, because Item_and (from @@ -1007,12 +1112,23 @@ Item_in_subselect::single_value_transformer(JOIN *join, DBUG_RETURN(RES_ERROR); item= new Item_cond_or(item, new Item_func_isnull(orig_item)); -#ifdef CORRECT_BUT_TOO_SLOW_TO_BE_USABLE - if (left_expr->maybe_null) - item= new Item_cond_or(new Item_func_isnull(left_expr), item); -#endif } + /* + If we may encounter NULL IN (SELECT ...) and care whether subquery + result is NULL or FALSE, wrap condition in a trig_cond. + */ + if (!abort_on_null && left_expr->maybe_null) + { + if (!(item= new Item_func_trig_cond(item, get_cond_guard(0)))) + DBUG_RETURN(RES_ERROR); + } + /* + TODO: figure out why the following is done here in + single_value_transformer but there is no corresponding action in + row_value_transformer? + */ item->name= (char *)in_additional_cond; + /* AND can't be changed during fix_fields() we can assign select_lex->having here, and pass 0 as last @@ -1037,15 +1153,22 @@ Item_in_subselect::single_value_transformer(JOIN *join, we can assign select_lex->having here, and pass 0 as last argument (reference) to fix_fields() */ - select_lex->having= - join->having= - func->create(expr, + Item *new_having= + func->create(expr, new Item_ref_null_helper(&select_lex->context, this, select_lex->ref_pointer_array, (char *)"<no matter>", (char *)"<result>")); - + if (!abort_on_null && left_expr->maybe_null) + { + if (!(new_having= new Item_func_trig_cond(new_having, + get_cond_guard(0)))) + DBUG_RETURN(RES_ERROR); + } + new_having->name= (char*)in_having_cond; + select_lex->having= join->having= new_having; select_lex->having_fix_field= 1; + /* we do not check join->having->fixed, because comparison function (from func->create) can't be fixed after creation @@ -1098,7 +1221,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) if (!substitution) { //first call for this unit - SELECT_LEX_UNIT *unit= select_lex->master_unit(); + SELECT_LEX_UNIT *master_unit= select_lex->master_unit(); substitution= optimizer; SELECT_LEX *current= thd->lex->current_select, *up; @@ -1114,7 +1237,16 @@ Item_in_subselect::row_value_transformer(JOIN *join) optimizer->keep_top_level_cache(); thd->lex->current_select= current; - unit->uncacheable|= UNCACHEABLE_DEPENDENT; + master_unit->uncacheable|= UNCACHEABLE_DEPENDENT; + + if (!abort_on_null && left_expr->maybe_null && !pushed_cond_guards) + { + if (!(pushed_cond_guards= (bool*)join->thd->alloc(sizeof(bool) * + left_expr->cols()))) + DBUG_RETURN(RES_ERROR); + for (uint i= 0; i < cols_num; i++) + pushed_cond_guards[i]= TRUE; + } } select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; @@ -1131,13 +1263,14 @@ Item_in_subselect::row_value_transformer(JOIN *join) is_not_null_test(v3)) where is_not_null_test used to register nulls in case if we have not found matching to return correct NULL value + TODO: say here explicitly if the order of AND parts matters or not. */ Item *item_having_part2= 0; for (uint i= 0; i < cols_num; i++) { DBUG_ASSERT(left_expr->fixed && select_lex->ref_pointer_array[i]->fixed); if (select_lex->ref_pointer_array[i]-> - check_cols(left_expr->el(i)->cols())) + check_cols(left_expr->element_index(i)->cols())) DBUG_RETURN(RES_ERROR); Item *item_eq= new Item_func_eq(new @@ -1159,21 +1292,28 @@ Item_in_subselect::row_value_transformer(JOIN *join) (char *)"<no matter>", (char *)"<list ref>") ); - having_item= - and_items(having_item, - new Item_cond_or(item_eq, item_isnull)); - item_having_part2= - and_items(item_having_part2, - new - Item_is_not_null_test(this, - new - Item_ref(&select_lex->context, - select_lex-> - ref_pointer_array + i, - (char *)"<no matter>", - (char *)"<list ref>") - ) - ); + Item *col_item= new Item_cond_or(item_eq, item_isnull); + if (!abort_on_null && left_expr->element_index(i)->maybe_null) + { + if (!(col_item= new Item_func_trig_cond(col_item, get_cond_guard(i)))) + DBUG_RETURN(RES_ERROR); + } + having_item= and_items(having_item, col_item); + + Item *item_nnull_test= + new Item_is_not_null_test(this, + new Item_ref(&select_lex->context, + select_lex-> + ref_pointer_array + i, + (char *)"<no matter>", + (char *)"<list ref>")); + if (!abort_on_null && left_expr->element_index(i)->maybe_null) + { + if (!(item_nnull_test= + new Item_func_trig_cond(item_nnull_test, get_cond_guard(i)))) + DBUG_RETURN(RES_ERROR); + } + item_having_part2= and_items(item_having_part2, item_nnull_test); item_having_part2->top_level_item(); } having_item= and_items(having_item, item_having_part2); @@ -1204,7 +1344,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) Item *item, *item_isnull; DBUG_ASSERT(left_expr->fixed && select_lex->ref_pointer_array[i]->fixed); if (select_lex->ref_pointer_array[i]-> - check_cols(left_expr->el(i)->cols())) + check_cols(left_expr->element_index(i)->cols())) DBUG_RETURN(RES_ERROR); item= new Item_func_eq(new @@ -1222,18 +1362,15 @@ Item_in_subselect::row_value_transformer(JOIN *join) ); if (!abort_on_null) { - having_item= - and_items(having_item, - new - Item_is_not_null_test(this, - new - Item_ref(&select_lex->context, - select_lex-> - ref_pointer_array + i, - (char *)"<no matter>", - (char *)"<list ref>") - ) - ); + Item *having_col_item= + new Item_is_not_null_test(this, + new + Item_ref(&select_lex->context, + select_lex->ref_pointer_array + i, + (char *)"<no matter>", + (char *)"<list ref>")); + + item_isnull= new Item_func_isnull(new Item_direct_ref(&select_lex->context, @@ -1242,10 +1379,21 @@ Item_in_subselect::row_value_transformer(JOIN *join) (char *)"<no matter>", (char *)"<list ref>") ); - item= new Item_cond_or(item, item_isnull); + /* + TODO: why we create the above for cases where the right part + cant be NULL? + */ + if (left_expr->element_index(i)->maybe_null) + { + if (!(item= new Item_func_trig_cond(item, get_cond_guard(i)))) + DBUG_RETURN(RES_ERROR); + if (!(having_col_item= + new Item_func_trig_cond(having_col_item, get_cond_guard(i)))) + DBUG_RETURN(RES_ERROR); + } + having_item= and_items(having_item, having_col_item); } - where_item= and_items(where_item, item); } /* @@ -1262,6 +1410,8 @@ Item_in_subselect::row_value_transformer(JOIN *join) { bool res; select_lex->having= join->having= and_items(join->having, having_item); + if (having_item == select_lex->having) + having_item->name= (char*)in_having_cond; select_lex->having->top_level_item(); /* AND can't be changed during fix_fields() @@ -1396,14 +1546,14 @@ void Item_in_subselect::print(String *str) } -bool Item_in_subselect::fix_fields(THD *thd, Item **ref) +bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref) { bool result = 0; - if(thd->lex->view_prepare_mode && left_expr && !left_expr->fixed) - result = left_expr->fix_fields(thd, &left_expr); + if (thd_arg->lex->view_prepare_mode && left_expr && !left_expr->fixed) + result = left_expr->fix_fields(thd_arg, &left_expr); - return result || Item_subselect::fix_fields(thd, ref); + return result || Item_subselect::fix_fields(thd_arg, ref); } @@ -1442,13 +1592,13 @@ void subselect_engine::set_thd(THD *thd_arg) subselect_single_select_engine:: subselect_single_select_engine(st_select_lex *select, - select_subselect *result, - Item_subselect *item) - :subselect_engine(item, result), + select_subselect *result_arg, + Item_subselect *item_arg) + :subselect_engine(item_arg, result_arg), prepared(0), optimized(0), executed(0), select_lex(select), join(0) { - select_lex->master_unit()->item= item; + select_lex->master_unit()->item= item_arg; } @@ -1477,6 +1627,27 @@ bool subselect_union_engine::is_executed() const } +/* + Check if last execution of the subquery engine produced any rows + + SYNOPSIS + subselect_union_engine::no_rows() + + DESCRIPTION + Check if last execution of the subquery engine produced any rows. The + return value is undefined if last execution ended in an error. + + RETURN + TRUE - Last subselect execution has produced no rows + FALSE - Otherwise +*/ + +bool subselect_union_engine::no_rows() +{ + /* Check if we got any rows when reading UNION result from temp. table: */ + return test(!unit->fake_select_lex->join->send_records); +} + void subselect_uniquesubquery_engine::cleanup() { DBUG_ENTER("subselect_uniquesubquery_engine::cleanup"); @@ -1542,32 +1713,58 @@ int subselect_uniquesubquery_engine::prepare() return 1; } -static Item_result set_row(List<Item> &item_list, Item *item, - Item_cache **row, bool *maybe_null) + +/* + Check if last execution of the subquery engine produced any rows + + SYNOPSIS + subselect_single_select_engine::no_rows() + + DESCRIPTION + Check if last execution of the subquery engine produced any rows. The + return value is undefined if last execution ended in an error. + + RETURN + TRUE - Last subselect execution has produced no rows + FALSE - Otherwise +*/ + +bool subselect_single_select_engine::no_rows() +{ + return !item->assigned(); +} + + +/* + makes storage for the output values for the subquery and calcuates + their data and column types and their nullability. +*/ +void subselect_engine::set_row(List<Item> &item_list, Item_cache **row) { - Item_result res_type= STRING_RESULT; Item *sel_item; List_iterator_fast<Item> li(item_list); + res_type= STRING_RESULT; + res_field_type= MYSQL_TYPE_VAR_STRING; for (uint i= 0; (sel_item= li++); i++) { item->max_length= sel_item->max_length; res_type= sel_item->result_type(); + res_field_type= sel_item->field_type(); item->decimals= sel_item->decimals; item->unsigned_flag= sel_item->unsigned_flag; - *maybe_null= sel_item->maybe_null; + maybe_null= sel_item->maybe_null; if (!(row[i]= Item_cache::get_cache(res_type))) - return STRING_RESULT; // we should return something + return; row[i]->setup(sel_item); } if (item_list.elements > 1) res_type= ROW_RESULT; - return res_type; } void subselect_single_select_engine::fix_length_and_dec(Item_cache **row) { DBUG_ASSERT(row || select_lex->item_list.elements==1); - res_type= set_row(select_lex->item_list, item, row, &maybe_null); + set_row(select_lex->item_list, row); item->collation.set(row[0]->collation); if (cols() != 1) maybe_null= 0; @@ -1579,13 +1776,14 @@ void subselect_union_engine::fix_length_and_dec(Item_cache **row) if (unit->first_select()->item_list.elements == 1) { - res_type= set_row(unit->types, item, row, &maybe_null); + set_row(unit->types, row); item->collation.set(row[0]->collation); } else { - bool fake= 0; - res_type= set_row(unit->types, item, row, &fake); + bool maybe_null_saved= maybe_null; + set_row(unit->types, row); + maybe_null= maybe_null_saved; } } @@ -1595,6 +1793,10 @@ void subselect_uniquesubquery_engine::fix_length_and_dec(Item_cache **row) DBUG_ASSERT(0); } +int init_read_record_seq(JOIN_TAB *tab); +int join_read_always_key_or_null(JOIN_TAB *tab); +int join_read_next_same_or_null(READ_RECORD *info); + int subselect_single_select_engine::exec() { DBUG_ENTER("subselect_single_select_engine::exec"); @@ -1633,7 +1835,50 @@ int subselect_single_select_engine::exec() if (!executed) { item->reset_value_registration(); + JOIN_TAB *changed_tabs[MAX_TABLES]; + JOIN_TAB **last_changed_tab= changed_tabs; + if (item->have_guarded_conds()) + { + /* + For at least one of the pushed predicates the following is true: + We should not apply optimizations based on the condition that was + pushed down into the subquery. Those optimizations are ref[_or_null] + acceses. Change them to be full table scans. + */ + for (uint i=join->const_tables ; i < join->tables ; i++) + { + JOIN_TAB *tab=join->join_tab+i; + if (tab && tab->keyuse) + { + for (uint i= 0; i < tab->ref.key_parts; i++) + { + bool *cond_guard= tab->ref.cond_guards[i]; + if (cond_guard && !*cond_guard) + { + /* Change the access method to full table scan */ + tab->read_first_record= init_read_record_seq; + tab->read_record.record= tab->table->record[0]; + tab->read_record.thd= join->thd; + tab->read_record.ref_length= tab->table->file->ref_length; + *(last_changed_tab++)= tab; + break; + } + } + } + } + } + join->exec(); + + /* Enable the optimizations back */ + for (JOIN_TAB **ptab= changed_tabs; ptab != last_changed_tab; ptab++) + { + JOIN_TAB *tab= *ptab; + tab->read_record.record= 0; + tab->read_record.ref_length= 0; + tab->read_first_record= join_read_always_key_or_null; + tab->read_record.read_record= join_read_next_same_or_null; + } executed= 1; thd->where= save_where; thd->lex->current_select= save_select; @@ -1653,20 +1898,149 @@ int subselect_union_engine::exec() } -int subselect_uniquesubquery_engine::exec() +/* + Search for at least one row satisfying select condition + + SYNOPSIS + subselect_uniquesubquery_engine::scan_table() + + DESCRIPTION + Scan the table using sequential access until we find at least one row + satisfying select condition. + + The caller must set this->empty_result_set=FALSE before calling this + function. This function will set it to TRUE if it finds a matching row. + + RETURN + FALSE - OK + TRUE - Error +*/ + +int subselect_uniquesubquery_engine::scan_table() { - DBUG_ENTER("subselect_uniquesubquery_engine::exec"); int error; TABLE *table= tab->table; - for (store_key **copy=tab->ref.key_copy ; *copy ; copy++) + DBUG_ENTER("subselect_uniquesubquery_engine::scan_table"); + + if (table->file->inited) + table->file->ha_index_end(); + + table->file->ha_rnd_init(1); + table->file->extra_opt(HA_EXTRA_CACHE, + current_thd->variables.read_buff_size); + table->null_row= 0; + for (;;) + { + error=table->file->rnd_next(table->record[0]); + if (error && error != HA_ERR_END_OF_FILE) + { + error= report_error(table, error); + break; + } + /* No more rows */ + if (table->status) + break; + + if (!cond || cond->val_int()) + { + empty_result_set= FALSE; + break; + } + } + + table->file->ha_rnd_end(); + DBUG_RETURN(error != 0); +} + + +/* + Copy ref key and check for null parts in it + + SYNOPSIS + subselect_uniquesubquery_engine::copy_ref_key() + + DESCRIPTION + Copy ref key and check for null parts in it. + + RETURN + FALSE - ok, index lookup key without keys copied. + TRUE - an error occured while copying the key +*/ + +bool subselect_uniquesubquery_engine::copy_ref_key() +{ + DBUG_ENTER("subselect_uniquesubquery_engine::copy_ref_key"); + + for (store_key **copy= tab->ref.key_copy ; *copy ; copy++) { - if ((tab->ref.key_err= (*copy)->copy()) & 1) + tab->ref.key_err= (*copy)->copy(); + + /* + When there is a NULL part in the key we don't need to make index + lookup for such key thus we don't need to copy whole key. + If we later should do a sequential scan return OK. Fail otherwise. + + See also the comment for the subselect_uniquesubquery_engine::exec() + function. + */ + null_keypart= (*copy)->null_key; + bool top_level= ((Item_in_subselect *) item)->is_top_level_item(); + if (null_keypart && !top_level) + break; + if ((tab->ref.key_err) & 1 || (null_keypart && top_level)) { - table->status= STATUS_NOT_FOUND; + tab->table->status= STATUS_NOT_FOUND; DBUG_RETURN(1); } } + DBUG_RETURN(0); +} + +/* + Execute subselect + + SYNOPSIS + subselect_uniquesubquery_engine::exec() + + DESCRIPTION + Find rows corresponding to the ref key using index access. + If some part of the lookup key is NULL, then we're evaluating + NULL IN (SELECT ... ) + This is a special case, we don't need to search for NULL in the table, + instead, the result value is + - NULL if select produces empty row set + - FALSE otherwise. + + In some cases (IN subselect is a top level item, i.e. abort_on_null==TRUE) + the caller doesn't distinguish between NULL and FALSE result and we just + return FALSE. + Otherwise we make a full table scan to see if there is at least one + matching row. + + The result of this function (info about whether a row was found) is + stored in this->empty_result_set. + NOTE + + RETURN + FALSE - ok + TRUE - an error occured while scanning +*/ + +int subselect_uniquesubquery_engine::exec() +{ + DBUG_ENTER("subselect_uniquesubquery_engine::exec"); + int error; + TABLE *table= tab->table; + empty_result_set= TRUE; + + /* TODO: change to use of 'full_scan' here? */ + if (copy_ref_key()) + DBUG_RETURN(1); + + if (null_keypart) + DBUG_RETURN(scan_table()); + if (!table->file->inited) table->file->ha_index_init(tab->ref.key, 0); error= table->file->index_read(table->record[0], @@ -1679,9 +2053,13 @@ int subselect_uniquesubquery_engine::exec() { error= 0; table->null_row= 0; - ((Item_in_subselect *) item)->value= (!table->status && - (!cond || cond->val_int()) ? 1 : - 0); + if (!table->status && (!cond || cond->val_int())) + { + ((Item_in_subselect *) item)->value= 1; + empty_result_set= FALSE; + } + else + ((Item_in_subselect *) item)->value= 0; } DBUG_RETURN(error != 0); @@ -1695,14 +2073,68 @@ subselect_uniquesubquery_engine::~subselect_uniquesubquery_engine() } +/* + Index-lookup subselect 'engine' - run the subquery + + SYNOPSIS + subselect_uniquesubquery_engine:exec() + full_scan + + DESCRIPTION + The engine is used to resolve subqueries in form + + oe IN (SELECT key FROM tbl WHERE subq_where) + + The value of the predicate is calculated as follows: + 1. If oe IS NULL, this is a special case, do a full table scan on + table tbl and search for row that satisfies subq_where. If such + row is found, return NULL, otherwise return FALSE. + 2. Make an index lookup via key=oe, search for a row that satisfies + subq_where. If found, return TRUE. + 3. If check_null==TRUE, make another lookup via key=NULL, search for a + row that satisfies subq_where. If found, return NULL, otherwise + return FALSE. + + TODO + The step #1 can be optimized further when the index has several key + parts. Consider a subquery: + + (oe1, oe2) IN (SELECT keypart1, keypart2 FROM tbl WHERE subq_where) + + and suppose we need to evaluate it for {oe1, oe2}=={const1, NULL}. + Current code will do a full table scan and obtain correct result. There + is a better option: instead of evaluating + + SELECT keypart1, keypart2 FROM tbl WHERE subq_where (1) + + and checking if it has produced any matching rows, evaluate + + SELECT keypart2 FROM tbl WHERE subq_where AND keypart1=const1 (2) + + If this query produces a row, the result is NULL (as we're evaluating + "(const1, NULL) IN { (const1, X), ... }", which has a value of UNKNOWN, + i.e. NULL). If the query produces no rows, the result is FALSE. + + We currently evaluate (1) by doing a full table scan. (2) can be + evaluated by doing a "ref" scan on "keypart1=const1", which can be much + cheaper. We can use index statistics to quickly check whether "ref" scan + will be cheaper than full table scan. + + RETURN + 0 + 1 +*/ + int subselect_indexsubquery_engine::exec() { - DBUG_ENTER("subselect_indexsubselect_engine::exec"); + DBUG_ENTER("subselect_indexsubquery_engine::exec"); int error; bool null_finding= 0; TABLE *table= tab->table; ((Item_in_subselect *) item)->value= 0; + empty_result_set= TRUE; + null_keypart= 0; if (check_null) { @@ -1711,14 +2143,12 @@ int subselect_indexsubquery_engine::exec() ((Item_in_subselect *) item)->was_null= 0; } - for (store_key **copy=tab->ref.key_copy ; *copy ; copy++) - { - if ((tab->ref.key_err= (*copy)->copy()) & 1) - { - table->status= STATUS_NOT_FOUND; - DBUG_RETURN(1); - } - } + /* Copy the ref key and check for nulls... */ + if (copy_ref_key()) + DBUG_RETURN(1); + + if (null_keypart) + DBUG_RETURN(scan_table()); if (!table->file->inited) table->file->ha_index_init(tab->ref.key, 1); @@ -1736,8 +2166,9 @@ int subselect_indexsubquery_engine::exec() table->null_row= 0; if (!table->status) { - if (!cond || cond->val_int()) + if ((!cond || cond->val_int()) && (!having || having->val_int())) { + empty_result_set= FALSE; if (null_finding) ((Item_in_subselect *) item)->was_null= 1; else @@ -1880,11 +2311,16 @@ void subselect_indexsubquery_engine::print(String *str) str->append(key_info->name); if (check_null) str->append(STRING_WITH_LEN(" checking NULL")); - if (cond) + if (cond) { str->append(STRING_WITH_LEN(" where ")); cond->print(str); } + if (having) + { + str->append(STRING_WITH_LEN(" having ")); + having->print(str); + } str->append(')'); } @@ -1971,6 +2407,22 @@ bool subselect_single_select_engine::no_tables() /* + Check statically whether the subquery can return NULL + + SINOPSYS + subselect_single_select_engine::may_be_null() + + RETURN + FALSE can guarantee that the subquery never return NULL + TRUE otherwise +*/ +bool subselect_single_select_engine::may_be_null() +{ + return ((no_tables() && !join->conds && !join->having) ? maybe_null : 1); +} + + +/* Report about presence of tables in subquery SYNOPSIS diff --git a/sql/item_subselect.h b/sql/item_subselect.h index a72c6e85739..37264f2136f 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -60,6 +59,9 @@ public: /* subquery is transformed */ bool changed; + /* TRUE <=> The underlying SELECT is correlated w.r.t some ancestor select */ + bool is_correlated; + enum trans_res {RES_OK, RES_REDUCE, RES_ERROR}; enum subs_type {UNKNOWN_SUBS, SINGLEROW_SUBS, EXISTS_SUBS, IN_SUBS, ALL_SUBS, ANY_SUBS}; @@ -88,7 +90,7 @@ public: enum Type type() const; bool is_null() { - val_int(); + update_null_value(); return null_value; } bool fix_fields(THD *thd, Item **ref); @@ -102,6 +104,7 @@ public: Item *get_tmp_table_item(THD *thd); void update_used_tables(); void print(String *str); + virtual bool have_guarded_conds() { return FALSE; } bool change_engine(subselect_engine *eng) { old_engine= engine; @@ -114,6 +117,7 @@ public: single select and union subqueries only. */ bool is_evaluated() const; + bool is_uncacheable() const; /* Used by max/min subquery to initialize value presence registration @@ -123,6 +127,12 @@ public: enum_parsing_place place() { return parsing_place; } bool walk(Item_processor processor, bool walk_subquery, byte *arg); + /** + Get the SELECT_LEX structure associated with this Item. + @return the SELECT_LEX structure associated with this Item + */ + st_select_lex* get_select_lex(); + friend class select_subselect; friend class Item_in_optimizer; friend bool Item_field::fix_fields(THD *, Item **); @@ -156,15 +166,30 @@ public: my_decimal *val_decimal(my_decimal *); bool val_bool(); enum Item_result result_type() const; + enum_field_types field_type() const; void fix_length_and_dec(); uint cols(); - Item* el(uint i) { return my_reinterpret_cast(Item*)(row[i]); } + Item* element_index(uint i) { return my_reinterpret_cast(Item*)(row[i]); } Item** addr(uint i) { return (Item**)row + i; } bool check_cols(uint c); bool null_inside(); void bring_value(); + /** + This method is used to implement a special case of semantic tree + rewriting, mandated by a SQL:2003 exception in the specification. + The only caller of this method is handle_sql2003_note184_exception(), + see the code there for more details. + Note that this method breaks the object internal integrity, by + removing it's association with the corresponding SELECT_LEX, + making this object orphan from the parse tree. + No other method, beside the destructor, should be called on this + object, as it is now invalid. + @return the SELECT_LEX structure that was given in the constructor. + */ + st_select_lex* invalidate_and_restore_select_lex(); + friend class select_singlerow_subselect; }; @@ -216,7 +241,20 @@ public: friend class subselect_indexsubquery_engine; }; -/* IN subselect */ + +/* + IN subselect: this represents "left_exr IN (SELECT ...)" + + This class has: + - (as a descendant of Item_subselect) a "subquery execution engine" which + allows it to evaluate subqueries. (and this class participates in + execution by having was_null variable where part of execution result + is stored. + - Transformation methods (todo: more on this). + + This class is not used directly, it is "wrapped" into Item_in_optimizer + which provides some small bits of subquery evaluation. +*/ class Item_in_subselect :public Item_exists_subselect { @@ -232,12 +270,22 @@ protected: bool abort_on_null; bool transformed; public: + /* Used to trigger on/off conditions that were pushed down to subselect */ + bool *pushed_cond_guards; + + bool *get_cond_guard(int i) + { + return pushed_cond_guards ? pushed_cond_guards + i : NULL; + } + void set_cond_guard_var(int i, bool v) { pushed_cond_guards[i]= v; } + bool have_guarded_conds() { return test(pushed_cond_guards); } + Item_func_not_all *upper_item; // point on NOT/NOP before ALL/SOME subquery Item_in_subselect(Item * left_expr, st_select_lex *select_lex); Item_in_subselect() :Item_exists_subselect(), optimizer(0), abort_on_null(0), transformed(0), - upper_item(0) + pushed_cond_guards(NULL), upper_item(0) {} subs_type substype() { return IN_SUBS; } @@ -257,6 +305,7 @@ public: my_decimal *val_decimal(my_decimal *); bool val_bool(); void top_level_item() { abort_on_null=1; } + inline bool is_top_level_item() { return abort_on_null; } bool test_limit(st_select_lex_unit *unit); void print(String *str); bool fix_fields(THD *thd, Item **ref); @@ -292,6 +341,7 @@ protected: THD *thd; /* pointer to current THD */ Item_subselect *item; /* item, that use this engine */ enum Item_result res_type; /* type of results */ + enum_field_types res_field_type; /* column type of the results */ bool maybe_null; /* may be null (first item in select) */ public: @@ -301,6 +351,7 @@ public: result= res; item= si; res_type= STRING_RESULT; + res_field_type= MYSQL_TYPE_VAR_STRING; maybe_null= 0; } virtual ~subselect_engine() {}; // to satisfy compiler @@ -314,18 +365,44 @@ public: THD * get_thd() { return thd; } virtual int prepare()= 0; virtual void fix_length_and_dec(Item_cache** row)= 0; + /* + Execute the engine + + SYNOPSIS + exec() + + DESCRIPTION + Execute the engine. The result of execution is subquery value that is + either captured by previously set up select_result-based 'sink' or + stored somewhere by the exec() method itself. + + A required side effect: If at least one pushed-down predicate is + disabled, subselect_engine->no_rows() must return correct result after + the exec() call. + + RETURN + 0 - OK + 1 - Either an execution error, or the engine was "changed", and the + caller should call exec() again for the new engine. + */ virtual int exec()= 0; virtual uint cols()= 0; /* return number of columns in select */ virtual uint8 uncacheable()= 0; /* query is uncacheable */ enum Item_result type() { return res_type; } + enum_field_types field_type() { return res_field_type; } virtual void exclude()= 0; - bool may_be_null() { return maybe_null; }; + virtual bool may_be_null() { return maybe_null; }; virtual table_map upper_select_const_tables()= 0; static table_map calc_const_tables(TABLE_LIST *); virtual void print(String *str)= 0; virtual bool change_result(Item_subselect *si, select_subselect *result)= 0; virtual bool no_tables()= 0; virtual bool is_executed() const { return FALSE; } + /* Check if subquery produced any rows during last query execution */ + virtual bool no_rows() = 0; + +protected: + void set_row(List<Item> &item_list, Item_cache **row); }; @@ -351,7 +428,9 @@ public: void print (String *str); bool change_result(Item_subselect *si, select_subselect *result); bool no_tables(); + bool may_be_null(); bool is_executed() const { return executed; } + bool no_rows(); }; @@ -374,15 +453,41 @@ public: bool change_result(Item_subselect *si, select_subselect *result); bool no_tables(); bool is_executed() const; + bool no_rows(); }; struct st_join_table; + + +/* + A subquery execution engine that evaluates the subquery by doing one index + lookup in a unique index. + + This engine is used to resolve subqueries in forms + + outer_expr IN (SELECT tbl.unique_key FROM tbl WHERE subq_where) + + or, tuple-based: + + (oe1, .. oeN) IN (SELECT uniq_key_part1, ... uniq_key_partK + FROM tbl WHERE subqwhere) + + i.e. the subquery is a single table SELECT without GROUP BY, aggregate + functions, etc. +*/ + class subselect_uniquesubquery_engine: public subselect_engine { protected: st_join_table *tab; - Item *cond; + Item *cond; /* The WHERE condition of subselect */ + /* + TRUE<=> last execution produced empty set. Valid only when left + expression is NULL. + */ + bool empty_result_set; + bool null_keypart; /* TRUE <=> constructed search tuple has a NULL */ public: // constructor can assign THD because it will be called after JOIN::prepare @@ -404,20 +509,55 @@ public: void print (String *str); bool change_result(Item_subselect *si, select_subselect *result); bool no_tables(); + int scan_table(); + bool copy_ref_key(); + bool no_rows() { return empty_result_set; } }; class subselect_indexsubquery_engine: public subselect_uniquesubquery_engine { + /* FALSE for 'ref', TRUE for 'ref-or-null'. */ bool check_null; + /* + The "having" clause. This clause (further reffered to as "artificial + having") was inserted by subquery transformation code. It contains + Item(s) that have a side-effect: they record whether the subquery has + produced a row with NULL certain components. We need to use it for cases + like + (oe1, oe2) IN (SELECT t.key, t.no_key FROM t1) + where we do index lookup on t.key=oe1 but need also to check if there + was a row such that t.no_key IS NULL. + + NOTE: This is currently here and not in the uniquesubquery_engine. Ideally + it should have been in uniquesubquery_engine in order to allow execution of + subqueries like + + (oe1, oe2) IN (SELECT primary_key, non_key_maybe_null_field FROM tbl) + + We could use uniquesubquery_engine for the first component and let + Item_is_not_null_test( non_key_maybe_null_field) to handle the second. + + However, subqueries like the above are currently not handled by index + lookup-based subquery engines, the engine applicability check misses + them: it doesn't switch the engine for case of artificial having and + [eq_]ref access (only for artifical having + ref_or_null or no having). + The above example subquery is handled as a full-blown SELECT with eq_ref + access to one table. + + Due to this limitation, the "artificial having" currently needs to be + checked by only in indexsubquery_engine. + */ + Item *having; public: // constructor can assign THD because it will be called after JOIN::prepare - subselect_indexsubquery_engine(THD *thd, st_join_table *tab_arg, + subselect_indexsubquery_engine(THD *thd_arg, st_join_table *tab_arg, Item_subselect *subs, Item *where, - bool chk_null) - :subselect_uniquesubquery_engine(thd, tab_arg, subs, where), - check_null(chk_null) + Item *having_arg, bool chk_null) + :subselect_uniquesubquery_engine(thd_arg, tab_arg, subs, where), + check_null(chk_null), + having(having_arg) {} int exec(); void print (String *str); @@ -429,3 +569,9 @@ inline bool Item_subselect::is_evaluated() const return engine->is_executed(); } +inline bool Item_subselect::is_uncacheable() const +{ + return engine->uncacheable(); +} + + diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 73e2c5e6935..f34fc008186 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -322,9 +321,13 @@ void Item_sum::make_field(Send_field *tmp_field) if (args[0]->type() == Item::FIELD_ITEM && keep_field_type()) { ((Item_field*) args[0])->field->make_field(tmp_field); - tmp_field->db_name=(char*)""; - tmp_field->org_table_name=tmp_field->table_name=(char*)""; - tmp_field->org_col_name=tmp_field->col_name=name; + /* For expressions only col_name should be non-empty string. */ + char *empty_string= (char*)""; + tmp_field->db_name= empty_string; + tmp_field->org_table_name= empty_string; + tmp_field->table_name= empty_string; + tmp_field->org_col_name= empty_string; + tmp_field->col_name= name; if (maybe_null) tmp_field->flags&= ~NOT_NULL_FLAG; } @@ -397,7 +400,7 @@ Field *Item_sum::create_tmp_field(bool group, TABLE *table, Field *field; switch (result_type()) { case REAL_RESULT: - field= new Field_double(max_length, maybe_null, name, decimals); + field= new Field_double(max_length, maybe_null, name, decimals, TRUE); break; case INT_RESULT: field= new Field_longlong(max_length, maybe_null, name, unsigned_flag); @@ -903,6 +906,7 @@ bool Item_sum_distinct::setup(THD *thd) tree= new Unique(simple_raw_key_cmp, &tree_key_length, tree_key_length, thd->variables.max_heap_table_size); + is_evaluated= FALSE; DBUG_RETURN(tree == 0); } @@ -910,6 +914,7 @@ bool Item_sum_distinct::setup(THD *thd) bool Item_sum_distinct::add() { args[0]->save_in_field(table->field[0], FALSE); + is_evaluated= FALSE; if (!table->field[0]->is_null()) { DBUG_ASSERT(tree); @@ -939,6 +944,7 @@ void Item_sum_distinct::clear() DBUG_ASSERT(tree != 0); /* we always have a tree */ null_value= 1; tree->reset(); + is_evaluated= FALSE; DBUG_VOID_RETURN; } @@ -948,6 +954,7 @@ void Item_sum_distinct::cleanup() delete tree; tree= 0; table= 0; + is_evaluated= FALSE; } Item_sum_distinct::~Item_sum_distinct() @@ -959,16 +966,20 @@ Item_sum_distinct::~Item_sum_distinct() void Item_sum_distinct::calculate_val_and_count() { - count= 0; - val.traits->set_zero(&val); - /* - We don't have a tree only if 'setup()' hasn't been called; - this is the case of sql_select.cc:return_zero_rows. - */ - if (tree) + if (!is_evaluated) { - table->field[0]->set_notnull(); - tree->walk(item_sum_distinct_walk, (void*) this); + count= 0; + val.traits->set_zero(&val); + /* + We don't have a tree only if 'setup()' hasn't been called; + this is the case of sql_select.cc:return_zero_rows. + */ + if (tree) + { + table->field[0]->set_notnull(); + tree->walk(item_sum_distinct_walk, (void*) this); + } + is_evaluated= TRUE; } } @@ -1024,9 +1035,13 @@ Item_sum_avg_distinct::fix_length_and_dec() void Item_sum_avg_distinct::calculate_val_and_count() { - Item_sum_distinct::calculate_val_and_count(); - if (count) - val.traits->div(&val, count); + if (!is_evaluated) + { + Item_sum_distinct::calculate_val_and_count(); + if (count) + val.traits->div(&val, count); + is_evaluated= TRUE; + } } @@ -1048,7 +1063,7 @@ bool Item_sum_count::add() count++; else { - (void) args[0]->val_int(); + args[0]->update_null_value(); if (!args[0]->null_value) count++; } @@ -1065,6 +1080,7 @@ longlong Item_sum_count::val_int() void Item_sum_count::cleanup() { DBUG_ENTER("Item_sum_count::cleanup"); + count= 0; Item_sum_int::cleanup(); used_table_cache= ~(table_map) 0; DBUG_VOID_RETURN; @@ -1108,7 +1124,7 @@ Field *Item_sum_avg::create_tmp_field(bool group, TABLE *table, { /* We must store both value and counter in the temporary table in one field. - The easyest way is to do this is to store both value in a string + The easiest way is to do this is to store both value in a string and unpack on access. */ field= new Field_string(((hybrid_type == DECIMAL_RESULT) ? @@ -1119,7 +1135,7 @@ Field *Item_sum_avg::create_tmp_field(bool group, TABLE *table, field= new Field_new_decimal(max_length, maybe_null, name, decimals, unsigned_flag); else - field= new Field_double(max_length, maybe_null, name, decimals); + field= new Field_double(max_length, maybe_null, name, decimals, TRUE); if (field) field->init(table); return field; @@ -1156,7 +1172,7 @@ double Item_sum_avg::val_real() my_decimal *Item_sum_avg::val_decimal(my_decimal *val) { - my_decimal sum, cnt; + my_decimal sum_buff, cnt; const my_decimal *sum_dec; DBUG_ASSERT(fixed == 1); if (!count) @@ -1164,7 +1180,7 @@ my_decimal *Item_sum_avg::val_decimal(my_decimal *val) null_value=1; return NULL; } - sum_dec= Item_sum_sum::val_decimal(&sum); + sum_dec= Item_sum_sum::val_decimal(&sum_buff); int2my_decimal(E_DEC_FATAL_ERROR, count, 0, &cnt); my_decimal_div(E_DEC_FATAL_ERROR, val, sum_dec, &cnt, prec_increment); return val; @@ -1186,8 +1202,9 @@ String *Item_sum_avg::val_str(String *str) double Item_sum_std::val_real() { DBUG_ASSERT(fixed == 1); - double tmp= Item_sum_variance::val_real(); - return tmp <= 0.0 ? 0.0 : sqrt(tmp); + double nr= Item_sum_variance::val_real(); + DBUG_ASSERT(nr >= 0.0); + return sqrt(nr); } Item *Item_sum_std::copy_or_same(THD* thd) @@ -1201,40 +1218,77 @@ Item *Item_sum_std::copy_or_same(THD* thd) */ -Item_sum_variance::Item_sum_variance(THD *thd, Item_sum_variance *item): - Item_sum_num(thd, item), hybrid_type(item->hybrid_type), - cur_dec(item->cur_dec), count(item->count), sample(item->sample), - prec_increment(item->prec_increment) +/** + Variance implementation for floating-point implementations, without + catastrophic cancellation, from Knuth's _TAoCP_, 3rd ed, volume 2, pg232. + This alters the value at m, s, and increments count. +*/ + +/* + These two functions are used by the Item_sum_variance and the + Item_variance_field classes, which are unrelated, and each need to calculate + variance. The difference between the two classes is that the first is used + for a mundane SELECT, while the latter is used in a GROUPing SELECT. +*/ +static void variance_fp_recurrence_next(double *m, double *s, ulonglong *count, double nr) { - if (hybrid_type == DECIMAL_RESULT) + *count += 1; + + if (*count == 1) { - memcpy(dec_sum, item->dec_sum, sizeof(item->dec_sum)); - memcpy(dec_sqr, item->dec_sqr, sizeof(item->dec_sqr)); - for (int i=0; i<2; i++) - { - dec_sum[i].fix_buffer_pointer(); - dec_sqr[i].fix_buffer_pointer(); - } + *m= nr; + *s= 0; } else { - sum= item->sum; - sum_sqr= item->sum_sqr; + double m_kminusone= *m; + *m= m_kminusone + (nr - m_kminusone) / (double) *count; + *s= *s + (nr - m_kminusone) * (nr - *m); } } +static double variance_fp_recurrence_result(double s, ulonglong count, bool is_sample_variance) +{ + if (count == 1) + return 0.0; + + if (is_sample_variance) + return s / (count - 1); + + /* else, is a population variance */ + return s / count; +} + + +Item_sum_variance::Item_sum_variance(THD *thd, Item_sum_variance *item): + Item_sum_num(thd, item), hybrid_type(item->hybrid_type), + count(item->count), sample(item->sample), + prec_increment(item->prec_increment) +{ + recurrence_m= item->recurrence_m; + recurrence_s= item->recurrence_s; +} + + void Item_sum_variance::fix_length_and_dec() { DBUG_ENTER("Item_sum_variance::fix_length_and_dec"); maybe_null= null_value= 1; prec_increment= current_thd->variables.div_precincrement; + + /* + According to the SQL2003 standard (Part 2, Foundations; sec 10.9, + aggregate function; paragraph 7h of Syntax Rules), "the declared + type of the result is an implementation-defined aproximate numeric + type. + */ + hybrid_type= REAL_RESULT; + switch (args[0]->result_type()) { case REAL_RESULT: case STRING_RESULT: decimals= min(args[0]->decimals + 4, NOT_FIXED_DEC); - hybrid_type= REAL_RESULT; - sum= 0.0; break; case INT_RESULT: case DECIMAL_RESULT: @@ -1243,37 +1297,14 @@ void Item_sum_variance::fix_length_and_dec() decimals= min(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE); max_length= my_decimal_precision_to_length(precision, decimals, unsigned_flag); - cur_dec= 0; - hybrid_type= DECIMAL_RESULT; - my_decimal_set_zero(dec_sum); - my_decimal_set_zero(dec_sqr); - /* - The maxium value to usable for variance is DECIMAL_MAX_LENGTH/2 - becasue we need to be able to calculate in dec_bin_size1 - column_value * column_value - */ - f_scale0= args[0]->decimals; - f_precision0= min(args[0]->decimal_precision() + DECIMAL_LONGLONG_DIGITS, - DECIMAL_MAX_PRECISION); - f_scale1= min(args[0]->decimals * 2, DECIMAL_MAX_SCALE); - f_precision1= min(args[0]->decimal_precision()*2 + DECIMAL_LONGLONG_DIGITS, - DECIMAL_MAX_PRECISION); - dec_bin_size0= my_decimal_get_binary_size(f_precision0, f_scale0); - dec_bin_size1= my_decimal_get_binary_size(f_precision1, f_scale1); break; } case ROW_RESULT: default: DBUG_ASSERT(0); } - DBUG_PRINT("info", ("Type: %s (%d, %d)", - (hybrid_type == REAL_RESULT ? "REAL_RESULT" : - hybrid_type == DECIMAL_RESULT ? "DECIMAL_RESULT" : - hybrid_type == INT_RESULT ? "INT_RESULT" : - "--ILLEGAL!!!--"), - max_length, - (int)decimals)); + DBUG_PRINT("info", ("Type: REAL_RESULT (%d, %d)", max_length, (int)decimals)); DBUG_VOID_RETURN; } @@ -1284,6 +1315,11 @@ Item *Item_sum_variance::copy_or_same(THD* thd) } +/** + Create a new field to match the type of value we're expected to yield. + If we're grouping, then we need some space to serialize variables into, to + pass around. +*/ Field *Item_sum_variance::create_tmp_field(bool group, TABLE *table, uint convert_blob_len) { @@ -1292,110 +1328,68 @@ Field *Item_sum_variance::create_tmp_field(bool group, TABLE *table, { /* We must store both value and counter in the temporary table in one field. - The easyest way is to do this is to store both value in a string + The easiest way is to do this is to store both value in a string and unpack on access. */ - field= new Field_string(((hybrid_type == DECIMAL_RESULT) ? - dec_bin_size0 + dec_bin_size1 : - sizeof(double)*2) + sizeof(longlong), - 0, name, &my_charset_bin); + field= new Field_string(sizeof(double)*2 + sizeof(longlong), 0, name, &my_charset_bin); } else - { - field= new Field_double(max_length, maybe_null,name, decimals); - } - if (field) + field= new Field_double(max_length, maybe_null, name, decimals, TRUE); + + if (field != NULL) field->init(table); + return field; } void Item_sum_variance::clear() { - if (hybrid_type == DECIMAL_RESULT) - { - my_decimal_set_zero(dec_sum); - my_decimal_set_zero(dec_sqr); - cur_dec= 0; - } - else - sum=sum_sqr=0.0; - count=0; + count= 0; } bool Item_sum_variance::add() { - if (hybrid_type == DECIMAL_RESULT) - { - my_decimal dec_buf, *dec= args[0]->val_decimal(&dec_buf); - my_decimal sqr_buf; - if (!args[0]->null_value) - { - count++; - int next_dec= cur_dec ^ 1; - my_decimal_mul(E_DEC_FATAL_ERROR, &sqr_buf, dec, dec); - my_decimal_add(E_DEC_FATAL_ERROR, dec_sqr+next_dec, - dec_sqr+cur_dec, &sqr_buf); - my_decimal_add(E_DEC_FATAL_ERROR, dec_sum+next_dec, - dec_sum+cur_dec, dec); - cur_dec= next_dec; - } - } - else - { - double nr= args[0]->val_real(); - if (!args[0]->null_value) - { - sum+=nr; - sum_sqr+=nr*nr; - count++; - } - } + /* + Why use a temporary variable? We don't know if it is null until we + evaluate it, which has the side-effect of setting null_value . + */ + double nr= args[0]->val_real(); + + if (!args[0]->null_value) + variance_fp_recurrence_next(&recurrence_m, &recurrence_s, &count, nr); return 0; } double Item_sum_variance::val_real() { DBUG_ASSERT(fixed == 1); - if (hybrid_type == DECIMAL_RESULT) - return val_real_from_decimal(); + /* + 'sample' is a 1/0 boolean value. If it is 1/true, id est this is a sample + variance call, then we should set nullness when the count of the items + is one or zero. If it's zero, i.e. a population variance, then we only + set nullness when the count is zero. + + Another way to read it is that 'sample' is the numerical threshhold, at and + below which a 'count' number of items is called NULL. + */ + DBUG_ASSERT((sample == 0) || (sample == 1)); if (count <= sample) { null_value=1; return 0.0; } + null_value=0; - /* Avoid problems when the precision isn't good enough */ - double tmp=ulonglong2double(count); - double tmp2= (sum_sqr - sum*sum/tmp)/(tmp - (double)sample); - return tmp2 <= 0.0 ? 0.0 : tmp2; + return variance_fp_recurrence_result(recurrence_s, count, sample); } my_decimal *Item_sum_variance::val_decimal(my_decimal *dec_buf) { - my_decimal count_buf, count1_buf, sum_sqr_buf; - DBUG_ASSERT(fixed ==1 ); - if (hybrid_type == REAL_RESULT) - return val_decimal_from_real(dec_buf); - - if (count <= sample) - { - null_value= 1; - return 0; - } - null_value= 0; - int2my_decimal(E_DEC_FATAL_ERROR, count, 0, &count_buf); - int2my_decimal(E_DEC_FATAL_ERROR, count-sample, 0, &count1_buf); - my_decimal_mul(E_DEC_FATAL_ERROR, &sum_sqr_buf, - dec_sum+cur_dec, dec_sum+cur_dec); - my_decimal_div(E_DEC_FATAL_ERROR, dec_buf, - &sum_sqr_buf, &count_buf, prec_increment); - my_decimal_sub(E_DEC_FATAL_ERROR, &sum_sqr_buf, dec_sqr+cur_dec, dec_buf); - my_decimal_div(E_DEC_FATAL_ERROR, dec_buf, - &sum_sqr_buf, &count1_buf, prec_increment); - return dec_buf; + DBUG_ASSERT(fixed == 1); + return val_decimal_from_real(dec_buf); } @@ -1404,89 +1398,44 @@ void Item_sum_variance::reset_field() double nr; char *res= result_field->ptr; - if (hybrid_type == DECIMAL_RESULT) - { - my_decimal value, *arg_dec, *arg2_dec; - longlong tmp; - - arg_dec= args[0]->val_decimal(&value); - if (args[0]->null_value) - { - arg_dec= arg2_dec= &decimal_zero; - tmp= 0; - } - else - { - my_decimal_mul(E_DEC_FATAL_ERROR, dec_sum, arg_dec, arg_dec); - arg2_dec= dec_sum; - tmp= 1; - } - my_decimal2binary(E_DEC_FATAL_ERROR, arg_dec, - res, f_precision0, f_scale0); - my_decimal2binary(E_DEC_FATAL_ERROR, arg2_dec, - res+dec_bin_size0, f_precision1, f_scale1); - res+= dec_bin_size0 + dec_bin_size1; - int8store(res,tmp); - return; - } - nr= args[0]->val_real(); + nr= args[0]->val_real(); /* sets null_value as side-effect */ if (args[0]->null_value) bzero(res,sizeof(double)*2+sizeof(longlong)); else { - longlong tmp; - float8store(res,nr); - nr*=nr; - float8store(res+sizeof(double),nr); - tmp= 1; - int8store(res+sizeof(double)*2,tmp); + /* Serialize format is (double)m, (double)s, (longlong)count */ + ulonglong tmp_count; + double tmp_s; + float8store(res, nr); /* recurrence variable m */ + tmp_s= 0.0; + float8store(res + sizeof(double), tmp_s); + tmp_count= 1; + int8store(res + sizeof(double)*2, tmp_count); } } void Item_sum_variance::update_field() { - longlong field_count; + ulonglong field_count; char *res=result_field->ptr; - if (hybrid_type == DECIMAL_RESULT) - { - my_decimal value, *arg_val= args[0]->val_decimal(&value); - if (!args[0]->null_value) - { - binary2my_decimal(E_DEC_FATAL_ERROR, res, - dec_sum+1, f_precision0, f_scale0); - binary2my_decimal(E_DEC_FATAL_ERROR, res+dec_bin_size0, - dec_sqr+1, f_precision1, f_scale1); - field_count= sint8korr(res + (dec_bin_size0 + dec_bin_size1)); - my_decimal_add(E_DEC_FATAL_ERROR, dec_sum, arg_val, dec_sum+1); - my_decimal_mul(E_DEC_FATAL_ERROR, dec_sum+1, arg_val, arg_val); - my_decimal_add(E_DEC_FATAL_ERROR, dec_sqr, dec_sqr+1, dec_sum+1); - field_count++; - my_decimal2binary(E_DEC_FATAL_ERROR, dec_sum, - res, f_precision0, f_scale0); - my_decimal2binary(E_DEC_FATAL_ERROR, dec_sqr, - res+dec_bin_size0, f_precision1, f_scale1); - res+= dec_bin_size0 + dec_bin_size1; - int8store(res, field_count); - } + + double nr= args[0]->val_real(); /* sets null_value as side-effect */ + + if (args[0]->null_value) return; - } - double nr,old_nr,old_sqr; - float8get(old_nr, res); - float8get(old_sqr, res+sizeof(double)); + /* Serialize format is (double)m, (double)s, (longlong)count */ + double field_recurrence_m, field_recurrence_s; + float8get(field_recurrence_m, res); + float8get(field_recurrence_s, res + sizeof(double)); field_count=sint8korr(res+sizeof(double)*2); - nr= args[0]->val_real(); - if (!args[0]->null_value) - { - old_nr+=nr; - old_sqr+=nr*nr; - field_count++; - } - float8store(res,old_nr); - float8store(res+sizeof(double),old_sqr); + variance_fp_recurrence_next(&field_recurrence_m, &field_recurrence_s, &field_count, nr); + + float8store(res, field_recurrence_m); + float8store(res + sizeof(double), field_recurrence_s); res+= sizeof(double)*2; int8store(res,field_count); } @@ -1678,7 +1627,7 @@ bool Item_sum_min::add() break; case DECIMAL_RESULT: { - my_decimal value, *val= args[0]->val_decimal(&value); + my_decimal value_buff, *val= args[0]->val_decimal(&value_buff); if (!args[0]->null_value && (null_value || (my_decimal_cmp(&sum_dec, val) > 0))) { @@ -1742,7 +1691,7 @@ bool Item_sum_max::add() break; case DECIMAL_RESULT: { - my_decimal value, *val= args[0]->val_decimal(&value); + my_decimal value_buff, *val= args[0]->val_decimal(&value_buff); if (!args[0]->null_value && (null_value || (my_decimal_cmp(val, &sum_dec) > 0))) { @@ -1907,7 +1856,7 @@ void Item_sum_hybrid::reset_field() } case DECIMAL_RESULT: { - my_decimal value, *arg_dec= args[0]->val_decimal(&value); + my_decimal value_buff, *arg_dec= args[0]->val_decimal(&value_buff); if (maybe_null) { @@ -1963,7 +1912,7 @@ void Item_sum_count::reset_field() nr=1; else { - (void) args[0]->val_int(); + args[0]->update_null_value(); if (!args[0]->null_value) nr=1; } @@ -2073,7 +2022,7 @@ void Item_sum_count::update_field() nr++; else { - (void) args[0]->val_int(); + args[0]->update_null_value(); if (!args[0]->null_value) nr++; } @@ -2313,25 +2262,9 @@ double Item_std_field::val_real() { double nr; // fix_fields() never calls for this Item - if (hybrid_type == REAL_RESULT) - { - /* - We can't call Item_variance_field::val_real() on a DECIMAL_RESULT - as this would call Item_std_field::val_decimal() and we would - calculate sqrt() twice - */ - nr= Item_variance_field::val_real(); - } - else - { - my_decimal dec_buf,*dec; - dec= Item_variance_field::val_decimal(&dec_buf); - if (!dec) - nr= 0.0; // NULL; Return 0.0 - else - my_decimal2double(E_DEC_FATAL_ERROR, dec, &nr); - } - return nr <= 0.0 ? 0.0 : sqrt(nr); + nr= Item_variance_field::val_real(); + DBUG_ASSERT(nr >= 0.0); + return sqrt(nr); } @@ -2345,11 +2278,13 @@ my_decimal *Item_std_field::val_decimal(my_decimal *dec_buf) double nr; if (hybrid_type == REAL_RESULT) return val_decimal_from_real(dec_buf); + dec= Item_variance_field::val_decimal(dec_buf); if (!dec) return 0; my_decimal2double(E_DEC_FATAL_ERROR, dec, &nr); - nr= nr <= 0.0 ? 0.0 : sqrt(nr); + DBUG_ASSERT(nr >= 0.0); + nr= sqrt(nr); double2my_decimal(E_DEC_FATAL_ERROR, nr, &tmp_dec); my_decimal_round(E_DEC_FATAL_ERROR, &tmp_dec, decimals, FALSE, dec_buf); return dec_buf; @@ -2384,52 +2319,15 @@ double Item_variance_field::val_real() if (hybrid_type == DECIMAL_RESULT) return val_real_from_decimal(); - double sum,sum_sqr; - longlong count; - float8get(sum,field->ptr); - float8get(sum_sqr,(field->ptr+sizeof(double))); + double recurrence_s; + ulonglong count; + float8get(recurrence_s, (field->ptr + sizeof(double))); count=sint8korr(field->ptr+sizeof(double)*2); if ((null_value= (count <= sample))) return 0.0; - double tmp= (double) count; - double tmp2= (sum_sqr - sum*sum/tmp)/(tmp - (double)sample); - return tmp2 <= 0.0 ? 0.0 : tmp2; -} - - -String *Item_variance_field::val_str(String *str) -{ - if (hybrid_type == DECIMAL_RESULT) - return val_string_from_decimal(str); - return val_string_from_real(str); -} - - -my_decimal *Item_variance_field::val_decimal(my_decimal *dec_buf) -{ - // fix_fields() never calls for this Item - if (hybrid_type == REAL_RESULT) - return val_decimal_from_real(dec_buf); - - longlong count= sint8korr(field->ptr+dec_bin_size0+dec_bin_size1); - if ((null_value= (count <= sample))) - return 0; - - my_decimal dec_count, dec1_count, dec_sum, dec_sqr, tmp; - int2my_decimal(E_DEC_FATAL_ERROR, count, 0, &dec_count); - int2my_decimal(E_DEC_FATAL_ERROR, count-sample, 0, &dec1_count); - binary2my_decimal(E_DEC_FATAL_ERROR, field->ptr, - &dec_sum, f_precision0, f_scale0); - binary2my_decimal(E_DEC_FATAL_ERROR, field->ptr+dec_bin_size0, - &dec_sqr, f_precision1, f_scale1); - my_decimal_mul(E_DEC_FATAL_ERROR, &tmp, &dec_sum, &dec_sum); - my_decimal_div(E_DEC_FATAL_ERROR, dec_buf, &tmp, &dec_count, prec_increment); - my_decimal_sub(E_DEC_FATAL_ERROR, &dec_sum, &dec_sqr, dec_buf); - my_decimal_div(E_DEC_FATAL_ERROR, dec_buf, - &dec_sum, &dec1_count, prec_increment); - return dec_buf; + return variance_fp_recurrence_result(recurrence_s, count, sample); } @@ -2496,6 +2394,7 @@ void Item_sum_count_distinct::cleanup() */ delete tree; tree= 0; + is_evaluated= FALSE; if (table) { free_tmp_table(table->in_use, table); @@ -2517,6 +2416,7 @@ void Item_sum_count_distinct::make_unique() original= 0; force_copy_fields= 1; tree= 0; + is_evaluated= FALSE; tmp_table_param= 0; always_null= FALSE; } @@ -2551,7 +2451,7 @@ bool Item_sum_count_distinct::setup(THD *thd) return TRUE; // End of memory if (item->const_item()) { - (void) item->val_int(); + item->update_null_value(); if (item->null_value) always_null=1; } @@ -2584,11 +2484,11 @@ bool Item_sum_count_distinct::setup(THD *thd) for (tree_key_length= 0; field < field_end; ++field) { Field *f= *field; - enum enum_field_types type= f->type(); + enum enum_field_types f_type= f->type(); tree_key_length+= f->pack_length(); - if ((type == MYSQL_TYPE_VARCHAR) || - !f->binary() && (type == MYSQL_TYPE_STRING || - type == MYSQL_TYPE_VAR_STRING)) + if ((f_type == MYSQL_TYPE_VARCHAR) || + !f->binary() && (f_type == MYSQL_TYPE_STRING || + f_type == MYSQL_TYPE_VAR_STRING)) { all_binary= FALSE; break; @@ -2636,6 +2536,7 @@ bool Item_sum_count_distinct::setup(THD *thd) but this has to be handled - otherwise someone can crash the server with a DoS attack */ + is_evaluated= FALSE; if (! tree) return TRUE; } @@ -2652,8 +2553,11 @@ Item *Item_sum_count_distinct::copy_or_same(THD* thd) void Item_sum_count_distinct::clear() { /* tree and table can be both null only if always_null */ + is_evaluated= FALSE; if (tree) + { tree->reset(); + } else if (table) { table->file->extra(HA_EXTRA_NO_CACHE); @@ -2674,6 +2578,7 @@ bool Item_sum_count_distinct::add() if ((*field)->is_real_null(0)) return 0; // Don't count NULL + is_evaluated= FALSE; if (tree) { /* @@ -2693,20 +2598,30 @@ bool Item_sum_count_distinct::add() longlong Item_sum_count_distinct::val_int() { + int error; DBUG_ASSERT(fixed == 1); if (!table) // Empty query return LL(0); if (tree) { - ulonglong count; + if (is_evaluated) + return count; if (tree->elements == 0) return (longlong) tree->elements_in_tree(); // everything fits in memory count= 0; tree->walk(count_distinct_walk, (void*) &count); + is_evaluated= TRUE; return (longlong) count; } - table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); + + error= table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); + + if(error) + { + table->file->print_error(error, MYF(0)); + } + return table->file->stats.records; } @@ -2920,13 +2835,14 @@ int group_concat_key_cmp_with_distinct(void* arg, byte* key1, */ Field *field= (*field_item)->get_tmp_table_field(); /* - If field_item is a const item then either get_tp_table_field returns 0 + If field_item is a const item then either get_tmp_table_field returns 0 or it is an item over a const table. */ if (field && !(*field_item)->const_item()) { int res; - uint offset= field->offset() - table->s->null_bytes; + uint offset= (field->offset(field->table->record[0]) - + table->s->null_bytes); if ((res= field->cmp((char *) key1 + offset, (char *) key2 + offset))) return res; } @@ -2964,7 +2880,8 @@ int group_concat_key_cmp_with_order(void* arg, byte* key1, byte* key2) if (field && !item->const_item()) { int res; - uint offset= field->offset() - table->s->null_bytes; + uint offset= (field->offset(field->table->record[0]) - + table->s->null_bytes); if ((res= field->cmp((char *) key1 + offset, (char *) key2 + offset))) return (*order_item)->asc ? res : -res; } @@ -3010,6 +2927,7 @@ int dump_leaf_key(byte* key, element_count count __attribute__((unused)), String tmp2; String *result= &item->result; Item **arg= item->args, **arg_end= item->args + item->arg_count_field; + uint old_length= result->length(); if (item->no_appended) item->no_appended= FALSE; @@ -3031,7 +2949,8 @@ int dump_leaf_key(byte* key, element_count count __attribute__((unused)), because it contains both order and arg list fields. */ Field *field= (*arg)->get_tmp_table_field(); - uint offset= field->offset() - table->s->null_bytes; + uint offset= (field->offset(field->table->record[0]) - + table->s->null_bytes); DBUG_ASSERT(offset < table->s->reclength); res= field->val_str(&tmp, (char *) key + offset); } @@ -3044,8 +2963,22 @@ int dump_leaf_key(byte* key, element_count count __attribute__((unused)), /* stop if length of result more than max_length */ if (result->length() > item->max_length) { + int well_formed_error; + CHARSET_INFO *cs= item->collation.collation; + const char *ptr= result->ptr(); + uint add_length; + /* + It's ok to use item->result.length() as the fourth argument + as this is never used to limit the length of the data. + Cut is done with the third argument. + */ + add_length= cs->cset->well_formed_len(cs, + ptr + old_length, + ptr + item->max_length, + result->length(), + &well_formed_error); + result->length(old_length + add_length); item->count_cut_values++; - result->length(item->max_length); item->warning_for_row= TRUE; return 1; } @@ -3140,8 +3073,6 @@ Item_func_group_concat::Item_func_group_concat(THD *thd, void Item_func_group_concat::cleanup() { - THD *thd= current_thd; - DBUG_ENTER("Item_func_group_concat::cleanup"); Item_sum::cleanup(); @@ -3150,7 +3081,7 @@ void Item_func_group_concat::cleanup() { char warn_buff[MYSQL_ERRMSG_SIZE]; sprintf(warn_buff, ER(ER_CUT_VALUE_GROUP_CONCAT), count_cut_values); - warning->set_msg(thd, warn_buff); + warning->set_msg(current_thd, warn_buff); warning= 0; } @@ -3180,8 +3111,7 @@ void Item_func_group_concat::cleanup() warning= 0; } } - DBUG_ASSERT(tree == 0); - DBUG_ASSERT(warning == 0); + DBUG_ASSERT(tree == 0 && warning == 0); } DBUG_VOID_RETURN; } @@ -3235,8 +3165,7 @@ bool Item_func_group_concat::add() we can dump the row here in case of GROUP_CONCAT(DISTINCT...) instead of doing tree traverse later. */ - if (result.length() <= max_length && - !warning_for_row && + if (!warning_for_row && (!tree || (el->count == 1 && distinct && !arg_count_order))) dump_leaf_key(table->record[0] + table->s->null_bytes, 1, this); @@ -3305,7 +3234,8 @@ bool Item_func_group_concat::setup(THD *thd) DBUG_RETURN(TRUE); /* We'll convert all blobs to varchar fields in the temporary table */ - tmp_table_param->convert_blob_length= max_length; + tmp_table_param->convert_blob_length= max_length * + collation.collation->mbmaxlen; /* Push all not constant fields to the list and create a temp table */ always_null= 0; for (uint i= 0; i < arg_count_field; i++) @@ -3382,8 +3312,8 @@ bool Item_func_group_concat::setup(THD *thd) duplicate values (according to the syntax of this function). If there is no DISTINCT or ORDER BY clauses, we don't create this tree. */ - init_tree(tree, min(thd->variables.max_heap_table_size, - thd->variables.sortbuff_size/16), 0, + init_tree(tree, (uint) min(thd->variables.max_heap_table_size, + thd->variables.sortbuff_size/16), 0, tree_key_length, compare_key, 0, NULL, (void*) this); } diff --git a/sql/item_sum.h b/sql/item_sum.h index 3679780db60..4cf16fc79af 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -223,7 +222,7 @@ class Item_sum :public Item_result_field public: enum Sumfunctype { COUNT_FUNC, COUNT_DISTINCT_FUNC, SUM_FUNC, SUM_DISTINCT_FUNC, AVG_FUNC, - AVG_DISTINCT_FUNC, MIN_FUNC, MAX_FUNC, UNIQUE_USERS_FUNC, STD_FUNC, + AVG_DISTINCT_FUNC, MIN_FUNC, MAX_FUNC, STD_FUNC, VARIANCE_FUNC, SUM_BIT_FUNC, UDF_SUM_FUNC, GROUP_CONCAT_FUNC }; @@ -260,9 +259,30 @@ public: Item_sum(THD *thd, Item_sum *item); enum Type type() const { return SUM_FUNC_ITEM; } virtual enum Sumfunctype sum_func () const=0; + + /* + This method is similar to add(), but it is called when the current + aggregation group changes. Thus it performs a combination of + clear() and add(). + */ inline bool reset() { clear(); return add(); }; + + /* + Prepare this item for evaluation of an aggregate value. This is + called by reset() when a group changes, or, for correlated + subqueries, between subquery executions. E.g. for COUNT(), this + method should set count= 0; + */ virtual void clear()= 0; + + /* + This method is called for the next row in the same group. Its + purpose is to aggregate the new value to the previous values in + the group (i.e. since clear() was called last time). For example, + for COUNT(), do count++. + */ virtual bool add()=0; + /* Called when new group is started and results are being saved in a temporary table. Similar to reset(), but must also store value in @@ -306,7 +326,17 @@ public: void make_field(Send_field *field); void print(String *str); void fix_num_length_and_dec(); - void no_rows_in_result() { reset(); } + + /* + This function is called by the execution engine to assign 'NO ROWS + FOUND' value to an aggregate item, when the underlying result set + has no rows. Such value, in a general case, may be different from + the default value of the item after 'clear()': e.g. a numeric item + may be initialized to 0 by clear() and to NULL by + no_rows_in_result(). + */ + void no_rows_in_result() { clear(); } + virtual bool setup(THD *thd) {return 0;} virtual void make_unique() {} Item *get_tmp_table_item(THD *thd); @@ -321,12 +351,23 @@ public: class Item_sum_num :public Item_sum { +protected: + /* + val_xxx() functions may be called several times during the execution of a + query. Derived classes that require extensive calculation in val_xxx() + maintain cache of aggregate value. This variable governs the validity of + that cache. + */ + bool is_evaluated; public: - Item_sum_num() :Item_sum() {} - Item_sum_num(Item *item_par) :Item_sum(item_par) {} - Item_sum_num(Item *a, Item* b) :Item_sum(a,b) {} - Item_sum_num(List<Item> &list) :Item_sum(list) {} - Item_sum_num(THD *thd, Item_sum_num *item) :Item_sum(thd, item) {} + Item_sum_num() :Item_sum(),is_evaluated(FALSE) {} + Item_sum_num(Item *item_par) + :Item_sum(item_par), is_evaluated(FALSE) {} + Item_sum_num(Item *a, Item* b) :Item_sum(a,b),is_evaluated(FALSE) {} + Item_sum_num(List<Item> &list) + :Item_sum(list), is_evaluated(FALSE) {} + Item_sum_num(THD *thd, Item_sum_num *item) + :Item_sum(thd, item),is_evaluated(item->is_evaluated) {} bool fix_fields(THD *, Item **); longlong val_int() { @@ -509,6 +550,12 @@ class Item_sum_count_distinct :public Item_sum_int */ Unique *tree; /* + Storage for the value of count between calls to val_int() so val_int() + will not recalculate on each call. Validitiy of the value is stored in + is_evaluated. + */ + longlong count; + /* Following is 0 normal object and pointer to original one for copy (to correctly free resources) */ @@ -525,14 +572,15 @@ class Item_sum_count_distinct :public Item_sum_int public: Item_sum_count_distinct(List<Item> &list) :Item_sum_int(list), table(0), field_lengths(0), tmp_table_param(0), - force_copy_fields(0), tree(0), original(0), always_null(FALSE) + force_copy_fields(0), tree(0), count(0), + original(0), always_null(FALSE) { quick_group= 0; } Item_sum_count_distinct(THD *thd, Item_sum_count_distinct *item) :Item_sum_int(thd, item), table(item->table), field_lengths(item->field_lengths), tmp_table_param(item->tmp_table_param), - force_copy_fields(0), tree(item->tree), original(item), - tree_key_length(item->tree_key_length), + force_copy_fields(0), tree(item->tree), count(item->count), + original(item), tree_key_length(item->tree_key_length), always_null(item->always_null) {} ~Item_sum_count_distinct(); @@ -569,7 +617,7 @@ public: double val_real(); longlong val_int(); my_decimal *val_decimal(my_decimal *); - bool is_null() { (void) val_int(); return null_value; } + bool is_null() { update_null_value(); return null_value; } String *val_str(String*); enum_field_types field_type() const { @@ -610,6 +658,11 @@ public: const char *func_name() const { return "avg("; } Item *copy_or_same(THD* thd); Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length); + void cleanup() + { + count= 0; + Item_sum_sum::cleanup(); + } }; class Item_sum_variance; @@ -629,9 +682,11 @@ public: double val_real(); longlong val_int() { /* can't be fix_fields()ed */ return (longlong) rint(val_real()); } - String *val_str(String*); - my_decimal *val_decimal(my_decimal *); - bool is_null() { (void) val_int(); return null_value; } + String *val_str(String *str) + { return val_string_from_real(str); } + my_decimal *val_decimal(my_decimal *dec_buf) + { return val_decimal_from_real(dec_buf); } + bool is_null() { update_null_value(); return null_value; } enum_field_types field_type() const { return hybrid_type == DECIMAL_RESULT ? @@ -652,6 +707,14 @@ public: = (sum(ai^2) - 2*sum(a)*sum(a)/count(a) + count(a)*sum(a)^2/count(a)^2 )/count(a) = = (sum(ai^2) - 2*sum(a)^2/count(a) + sum(a)^2/count(a) )/count(a) = = (sum(ai^2) - sum(a)^2/count(a))/count(a) + +But, this falls prey to catastrophic cancellation. Instead, use the recurrence formulas + + M_{1} = x_{1}, ~ M_{k} = M_{k-1} + (x_{k} - M_{k-1}) / k newline + S_{1} = 0, ~ S_{k} = S_{k-1} + (x_{k} - M_{k-1}) times (x_{k} - M_{k}) newline + for 2 <= k <= n newline + ital variance = S_{n} / (n-1) + */ class Item_sum_variance : public Item_sum_num @@ -660,9 +723,8 @@ class Item_sum_variance : public Item_sum_num public: Item_result hybrid_type; - double sum, sum_sqr; - my_decimal dec_sum[2], dec_sqr[2]; int cur_dec; + double recurrence_m, recurrence_s; /* Used in recurrence relation. */ ulonglong count; uint f_precision0, f_scale0; uint f_precision1, f_scale1; @@ -671,7 +733,7 @@ public: uint prec_increment; Item_sum_variance(Item *item_par, uint sample_arg) :Item_sum_num(item_par), - hybrid_type(REAL_RESULT), cur_dec(0), count(0), sample(sample_arg) + hybrid_type(REAL_RESULT), count(0), sample(sample_arg) {} Item_sum_variance(THD *thd, Item_sum_variance *item); enum Sumfunctype sum_func () const { return VARIANCE_FUNC; } @@ -689,6 +751,11 @@ public: Item *copy_or_same(THD* thd); Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length); enum Item_result result_type () const { return REAL_RESULT; } + void cleanup() + { + count= 0; + Item_sum_num::cleanup(); + } }; class Item_sum_std; @@ -744,7 +811,7 @@ protected: public: Item_sum_hybrid(Item *item_par,int sign) :Item_sum(item_par), sum(0.0), sum_int(0), - hybrid_type(INT_RESULT), hybrid_field_type(FIELD_TYPE_LONGLONG), + hybrid_type(INT_RESULT), hybrid_field_type(MYSQL_TYPE_LONGLONG), cmp_sign(sign), used_table_cache(~(table_map) 0), was_values(TRUE) { collation.set(&my_charset_bin); } @@ -819,6 +886,11 @@ public: void update_field(); void fix_length_and_dec() { decimals= 0; max_length=21; unsigned_flag= 1; maybe_null= null_value= 0; } + void cleanup() + { + bits= reset_bits; + Item_sum_int::cleanup(); + } }; @@ -1122,7 +1194,7 @@ public: enum_field_types field_type() const { if (max_length/collation.collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB ) - return FIELD_TYPE_BLOB; + return MYSQL_TYPE_BLOB; else return MYSQL_TYPE_VARCHAR; } diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index d32adde5e64..1bf1fec5cfb 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -96,6 +95,124 @@ static bool make_datetime(date_time_format_types format, TIME *ltime, /* + Wrapper over make_datetime() with validation of the input TIME value + + NOTE + see make_datetime() for more information + + RETURN + 1 if there was an error during converion + 0 otherwise +*/ + +static bool make_datetime_with_warn(date_time_format_types format, TIME *ltime, + String *str) +{ + int warning= 0; + + if (make_datetime(format, ltime, str)) + return 1; + if (check_time_range(ltime, &warning)) + return 1; + if (!warning) + return 0; + + make_truncated_value_warning(current_thd, str->ptr(), str->length(), + MYSQL_TIMESTAMP_TIME, NullS); + return make_datetime(format, ltime, str); +} + + +/* + Wrapper over make_time() with validation of the input TIME value + + NOTE + see make_time() for more info + + RETURN + 1 if there was an error during conversion + 0 otherwise +*/ + +static bool make_time_with_warn(const DATE_TIME_FORMAT *format, + TIME *l_time, String *str) +{ + int warning= 0; + make_time(format, l_time, str); + if (check_time_range(l_time, &warning)) + return 1; + if (warning) + { + make_truncated_value_warning(current_thd, str->ptr(), str->length(), + MYSQL_TIMESTAMP_TIME, NullS); + make_time(format, l_time, str); + } + + return 0; +} + + +/* + Convert seconds to TIME value with overflow checking + + SYNOPSIS: + sec_to_time() + seconds number of seconds + unsigned_flag 1, if 'seconds' is unsigned, 0, otherwise + ltime output TIME value + + DESCRIPTION + If the 'seconds' argument is inside TIME data range, convert it to a + corresponding value. + Otherwise, truncate the resulting value to the nearest endpoint, and + produce a warning message. + + RETURN + 1 if the value was truncated during conversion + 0 otherwise +*/ + +static bool sec_to_time(longlong seconds, bool unsigned_flag, TIME *ltime) +{ + uint sec; + + bzero((char *)ltime, sizeof(*ltime)); + + if (seconds < 0) + { + if (unsigned_flag) + goto overflow; + ltime->neg= 1; + if (seconds < -3020399) + goto overflow; + seconds= -seconds; + } + else if (seconds > 3020399) + goto overflow; + + sec= (uint) ((ulonglong) seconds % 3600); + ltime->hour= (uint) (seconds/3600); + ltime->minute= sec/60; + ltime->second= sec % 60; + + return 0; + +overflow: + ltime->hour= TIME_MAX_HOUR; + ltime->minute= TIME_MAX_MINUTE; + ltime->second= TIME_MAX_SECOND; + + char buf[22]; + int len= (int)(longlong10_to_str(seconds, buf, unsigned_flag ? 10 : -10) + - buf); + make_truncated_value_warning(current_thd, buf, len, MYSQL_TIMESTAMP_TIME, + NullS); + + return 1; +} + + +/* Date formats corresponding to compound %r and %T conversion specifiers Note: We should init at least first element of "positions" array @@ -171,15 +288,16 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, for (; ptr != end && val != val_end; ptr++) { + /* Skip pre-space between each argument */ + while (val != val_end && my_isspace(cs, *val)) + val++; + if (*ptr == '%' && ptr+1 != end) { int val_len; char *tmp; error= 0; - /* Skip pre-space between each argument */ - while (val != val_end && my_isspace(cs, *val)) - val++; val_len= (uint) (val_end - val); switch (*++ptr) { @@ -490,16 +608,10 @@ bool make_date_time(DATE_TIME_FORMAT *format, TIME *l_time, uint weekday; ulong length; const char *ptr, *end; - MY_LOCALE *locale; THD *thd= current_thd; - char buf[STRING_BUFFER_USUAL_SIZE]; - String tmp(buf, sizeof(buf), thd->variables.character_set_results); - uint errors= 0; + MY_LOCALE *locale= thd->variables.lc_time_names; - tmp.length(0); str->length(0); - str->set_charset(&my_charset_bin); - locale = thd->variables.lc_time_names; if (l_time->neg) str->append('-'); @@ -513,41 +625,37 @@ bool make_date_time(DATE_TIME_FORMAT *format, TIME *l_time, { switch (*++ptr) { case 'M': - if (!l_time->month) - return 1; - tmp.copy(locale->month_names->type_names[l_time->month-1], - strlen(locale->month_names->type_names[l_time->month-1]), - system_charset_info, tmp.charset(), &errors); - str->append(tmp.ptr(), tmp.length()); - break; + if (!l_time->month) + return 1; + str->append(locale->month_names->type_names[l_time->month-1], + strlen(locale->month_names->type_names[l_time->month-1]), + system_charset_info); + break; case 'b': - if (!l_time->month) - return 1; - tmp.copy(locale->ab_month_names->type_names[l_time->month-1], - strlen(locale->ab_month_names->type_names[l_time->month-1]), - system_charset_info, tmp.charset(), &errors); - str->append(tmp.ptr(), tmp.length()); - break; + if (!l_time->month) + return 1; + str->append(locale->ab_month_names->type_names[l_time->month-1], + strlen(locale->ab_month_names->type_names[l_time->month-1]), + system_charset_info); + break; case 'W': - if (type == MYSQL_TIMESTAMP_TIME) - return 1; - weekday= calc_weekday(calc_daynr(l_time->year,l_time->month, - l_time->day),0); - tmp.copy(locale->day_names->type_names[weekday], - strlen(locale->day_names->type_names[weekday]), - system_charset_info, tmp.charset(), &errors); - str->append(tmp.ptr(), tmp.length()); - break; + if (type == MYSQL_TIMESTAMP_TIME) + return 1; + weekday= calc_weekday(calc_daynr(l_time->year,l_time->month, + l_time->day),0); + str->append(locale->day_names->type_names[weekday], + strlen(locale->day_names->type_names[weekday]), + system_charset_info); + break; case 'a': - if (type == MYSQL_TIMESTAMP_TIME) - return 1; - weekday=calc_weekday(calc_daynr(l_time->year,l_time->month, - l_time->day),0); - tmp.copy(locale->ab_day_names->type_names[weekday], - strlen(locale->ab_day_names->type_names[weekday]), - system_charset_info, tmp.charset(), &errors); - str->append(tmp.ptr(), tmp.length()); - break; + if (type == MYSQL_TIMESTAMP_TIME) + return 1; + weekday=calc_weekday(calc_daynr(l_time->year,l_time->month, + l_time->day),0); + str->append(locale->ab_day_names->type_names[weekday], + strlen(locale->ab_day_names->type_names[weekday]), + system_charset_info); + break; case 'D': if (type == MYSQL_TIMESTAMP_TIME) return 1; @@ -899,7 +1007,8 @@ longlong Item_func_quarter::val_int() { DBUG_ASSERT(fixed == 1); TIME ltime; - (void) get_arg0_date(<ime, TIME_FUZZY_DATE); + if (get_arg0_date(<ime, TIME_FUZZY_DATE)) + return 0; return (longlong) ((ltime.month+2)/3); } @@ -1011,14 +1120,14 @@ String* Item_func_dayname::val_str(String* str) { DBUG_ASSERT(fixed == 1); uint weekday=(uint) val_int(); // Always Item_func_daynr() - const char *name; + const char *day_name; THD *thd= current_thd; if (null_value) return (String*) 0; - name= thd->variables.lc_time_names->day_names->type_names[weekday]; - str->set(name, strlen(name), system_charset_info); + day_name= thd->variables.lc_time_names->day_names->type_names[weekday]; + str->set(day_name, strlen(day_name), system_charset_info); return str; } @@ -1066,7 +1175,7 @@ longlong Item_func_unix_timestamp::val_int() if (args[0]->type() == FIELD_ITEM) { // Optimize timestamp field Field *field=((Item_field*) args[0])->field; - if (field->type() == FIELD_TYPE_TIMESTAMP) + if (field->type() == MYSQL_TYPE_TIMESTAMP) return ((Field_timestamp*) field)->get_timestamp(&null_value); } @@ -1249,6 +1358,9 @@ bool get_interval_value(Item *args,interval_type int_type, interval->second= array[0]; interval->second_part= array[1]; break; + case INTERVAL_LAST: /* purecov: begin deadcode */ + DBUG_ASSERT(0); + break; /* purecov: end */ } return 0; } @@ -1270,17 +1382,6 @@ String *Item_date::val_str(String *str) } -int Item_date::save_in_field(Field *field, bool no_conversions) -{ - TIME ltime; - if (get_date(<ime, TIME_FUZZY_DATE)) - return set_field_to_null(field); - field->set_notnull(); - field->store_time(<ime, MYSQL_TIMESTAMP_DATE); - return 0; -} - - longlong Item_date::val_int() { DBUG_ASSERT(fixed == 1); @@ -1486,7 +1587,7 @@ int Item_func_now::save_in_field(Field *to, bool no_conversions) void Item_func_sysdate_local::store_now_in_TIME(TIME *now_time) { THD *thd= current_thd; - thd->variables.time_zone->gmt_sec_to_TIME(now_time, time(NULL)); + thd->variables.time_zone->gmt_sec_to_TIME(now_time, (my_time_t) time(NULL)); thd->time_zone_used= 1; } @@ -1546,9 +1647,8 @@ int Item_func_sysdate_local::save_in_field(Field *to, bool no_conversions) String *Item_func_sec_to_time::val_str(String *str) { DBUG_ASSERT(fixed == 1); - longlong seconds=(longlong) args[0]->val_int(); - uint sec; TIME ltime; + longlong arg_val= args[0]->val_int(); if ((null_value=args[0]->null_value) || str->alloc(19)) { @@ -1556,19 +1656,8 @@ String *Item_func_sec_to_time::val_str(String *str) return (String*) 0; } - ltime.neg= 0; - if (seconds < 0) - { - seconds= -seconds; - ltime.neg= 1; - } - - sec= (uint) ((ulonglong) seconds % 3600); - ltime.day= 0; - ltime.hour= (uint) (seconds/3600); - ltime.minute= sec/60; - ltime.second= sec % 60; - + sec_to_time(arg_val, args[0]->unsigned_flag, <ime); + make_time((DATE_TIME_FORMAT *) 0, <ime, str); return str; } @@ -1577,21 +1666,22 @@ String *Item_func_sec_to_time::val_str(String *str) longlong Item_func_sec_to_time::val_int() { DBUG_ASSERT(fixed == 1); - longlong seconds=args[0]->val_int(); - longlong sign=1; + TIME ltime; + longlong arg_val= args[0]->val_int(); + if ((null_value=args[0]->null_value)) return 0; - if (seconds < 0) - { - seconds= -seconds; - sign= -1; - } - return sign*((seconds / 3600)*10000+((seconds/60) % 60)*100+ (seconds % 60)); + + sec_to_time(arg_val, args[0]->unsigned_flag, <ime); + + return (ltime.neg ? -1 : 1) * + ((ltime.hour)*10000 + ltime.minute*100 + ltime.second); } void Item_func_date_format::fix_length_and_dec() { + THD* thd= current_thd; /* Must use this_item() in case it's a local SP variable (for ->max_length and ->str_value) @@ -1599,22 +1689,18 @@ void Item_func_date_format::fix_length_and_dec() Item *arg1= args[1]->this_item(); decimals=0; - collation.set(&my_charset_bin); + collation.set(thd->variables.collation_connection); if (arg1->type() == STRING_ITEM) { // Optimize the normal case fixed_length=1; - - /* - The result is a binary string (no reason to use collation->mbmaxlen - This is becasue make_date_time() only returns binary strings - */ - max_length= format_length(&arg1->str_value); + max_length= format_length(&arg1->str_value) * + collation.collation->mbmaxlen; } else { fixed_length=0; - /* The result is a binary string (no reason to use collation->mbmaxlen */ - max_length=min(arg1->max_length, MAX_BLOB_WIDTH) * 10; + max_length=min(arg1->max_length, MAX_BLOB_WIDTH) * 10 * + collation.collation->mbmaxlen; set_if_smaller(max_length,MAX_BLOB_WIDTH); } maybe_null=1; // If wrong date @@ -1759,6 +1845,7 @@ String *Item_func_date_format::val_str(String *str) date_time_format.format.length= format->length(); /* Create the result string */ + str->set_charset(collation.collation); if (!make_date_time(&date_time_format, &l_time, is_time_format ? MYSQL_TIMESTAMP_TIME : MYSQL_TIMESTAMP_DATE, @@ -1906,17 +1993,14 @@ bool Item_func_convert_tz::get_date(TIME *ltime, return 1; } - /* Check if we in range where we treat datetime values as non-UTC */ - if (ltime->year < TIMESTAMP_MAX_YEAR && ltime->year > TIMESTAMP_MIN_YEAR || - ltime->year==TIMESTAMP_MAX_YEAR && ltime->month==1 && ltime->day==1 || - ltime->year==TIMESTAMP_MIN_YEAR && ltime->month==12 && ltime->day==31) { my_bool not_used; my_time_tmp= from_tz->TIME_to_gmt_sec(ltime, ¬_used); - if (my_time_tmp >= TIMESTAMP_MIN_VALUE && my_time_tmp <= TIMESTAMP_MAX_VALUE) + /* my_time_tmp is guranteed to be in the allowed range */ + if (my_time_tmp) to_tz->gmt_sec_to_TIME(ltime, my_time_tmp); } - + null_value= 0; return 0; } @@ -2030,11 +2114,15 @@ bool Item_date_add_interval::eq(const Item *item, bool binary_cmp) const (date_sub_interval == other->date_sub_interval)); } +/* + 'interval_names' reflects the order of the enumeration interval_type. + See item_timefunc.h + */ static const char *interval_names[]= { - "year", "quarter", "month", "day", "hour", - "minute", "week", "second", "microsecond", + "year", "quarter", "month", "week", "day", + "hour", "minute", "second", "microsecond", "year_month", "day_hour", "day_minute", "day_second", "hour_minute", "hour_second", "minute_second", "day_microsecond", @@ -2088,6 +2176,7 @@ void Item_extract::fix_length_and_dec() case INTERVAL_HOUR_MICROSECOND: max_length=13; date_value=0; break; case INTERVAL_MINUTE_MICROSECOND: max_length=11; date_value=0; break; case INTERVAL_SECOND_MICROSECOND: max_length=9; date_value=0; break; + case INTERVAL_LAST: DBUG_ASSERT(0); break; /* purecov: deadcode */ } } @@ -2157,6 +2246,7 @@ longlong Item_extract::val_int() ltime.second_part)*neg; case INTERVAL_SECOND_MICROSECOND: return ((longlong)ltime.second*1000000L+ ltime.second_part)*neg; + case INTERVAL_LAST: DBUG_ASSERT(0); break; /* purecov: deadcode */ } return 0; // Impossible } @@ -2271,7 +2361,8 @@ String *Item_char_typecast::val_str(String *str) { // Safe even if const arg char char_type[40]; my_snprintf(char_type, sizeof(char_type), "%s(%lu)", - cast_cs == &my_charset_bin ? "BINARY" : "CHAR", length); + cast_cs == &my_charset_bin ? "BINARY" : "CHAR", + (ulong) length); if (!res->alloced_length()) { // Don't change const str @@ -2440,7 +2531,10 @@ longlong Item_date_typecast::val_int() DBUG_ASSERT(fixed == 1); TIME ltime; if (args[0]->get_date(<ime, TIME_FUZZY_DATE)) + { + null_value= 1; return 0; + } return (longlong) (ltime.year * 10000L + ltime.month * 100 + ltime.day); } @@ -2571,7 +2665,9 @@ String *Item_func_add_time::val_str(String *str) } if (l_time1.neg != l_time2.neg) l_sign= -l_sign; - + + bzero((char *)&l_time3, sizeof(l_time3)); + l_time3.neg= calc_time_diff(&l_time1, &l_time2, -l_sign, &seconds, µseconds); @@ -2600,9 +2696,9 @@ String *Item_func_add_time::val_str(String *str) } l_time3.hour+= days*24; - if (!make_datetime(l_time1.second_part || l_time2.second_part ? - TIME_MICROSECOND : TIME_ONLY, - &l_time3, str)) + if (!make_datetime_with_warn(l_time1.second_part || l_time2.second_part ? + TIME_MICROSECOND : TIME_ONLY, + &l_time3, str)) return str; null_date: @@ -2657,6 +2753,8 @@ String *Item_func_timediff::val_str(String *str) if (l_time1.neg != l_time2.neg) l_sign= -l_sign; + bzero((char *)&l_time3, sizeof(l_time3)); + l_time3.neg= calc_time_diff(&l_time1, &l_time2, l_sign, &seconds, µseconds); @@ -2670,9 +2768,9 @@ String *Item_func_timediff::val_str(String *str) calc_time_from_sec(&l_time3, (long) seconds, microseconds); - if (!make_datetime(l_time1.second_part || l_time2.second_part ? - TIME_MICROSECOND : TIME_ONLY, - &l_time3, str)) + if (!make_datetime_with_warn(l_time1.second_part || l_time2.second_part ? + TIME_MICROSECOND : TIME_ONLY, + &l_time3, str)) return str; null_date: @@ -2690,29 +2788,58 @@ String *Item_func_maketime::val_str(String *str) { DBUG_ASSERT(fixed == 1); TIME ltime; + bool overflow= 0; - long hour= (long) args[0]->val_int(); - long minute= (long) args[1]->val_int(); - long second= (long) args[2]->val_int(); + longlong hour= args[0]->val_int(); + longlong minute= args[1]->val_int(); + longlong second= args[2]->val_int(); if ((null_value=(args[0]->null_value || - args[1]->null_value || - args[2]->null_value || - minute > 59 || minute < 0 || - second > 59 || second < 0 || - str->alloc(19)))) + args[1]->null_value || + args[2]->null_value || + minute < 0 || minute > 59 || + second < 0 || second > 59 || + str->alloc(19)))) return 0; + bzero((char *)<ime, sizeof(ltime)); ltime.neg= 0; + + /* Check for integer overflows */ if (hour < 0) { - ltime.neg= 1; - hour= -hour; + if (args[0]->unsigned_flag) + overflow= 1; + else + ltime.neg= 1; + } + if (-hour > UINT_MAX || hour > UINT_MAX) + overflow= 1; + + if (!overflow) + { + ltime.hour= (uint) ((hour < 0 ? -hour : hour)); + ltime.minute= (uint) minute; + ltime.second= (uint) second; + } + else + { + ltime.hour= TIME_MAX_HOUR; + ltime.minute= TIME_MAX_MINUTE; + ltime.second= TIME_MAX_SECOND; + char buf[28]; + char *ptr= longlong10_to_str(hour, buf, args[0]->unsigned_flag ? 10 : -10); + int len = (int)(ptr - buf) + + my_sprintf(ptr, (ptr, ":%02u:%02u", (uint)minute, (uint)second)); + make_truncated_value_warning(current_thd, buf, len, MYSQL_TIMESTAMP_TIME, + NullS); + } + + if (make_time_with_warn((DATE_TIME_FORMAT *) 0, <ime, str)) + { + null_value= 1; + return 0; } - ltime.hour= (ulong) hour; - ltime.minute= (ulong) minute; - ltime.second= (ulong) second; - make_time((DATE_TIME_FORMAT *) 0, <ime, str); return str; } @@ -3047,20 +3174,22 @@ bool Item_func_str_to_date::get_date(TIME *ltime, uint fuzzy_date) { DATE_TIME_FORMAT date_time_format; char val_buff[64], format_buff[64]; - String val_str(val_buff, sizeof(val_buff), &my_charset_bin), *val; + String val_string(val_buff, sizeof(val_buff), &my_charset_bin), *val; String format_str(format_buff, sizeof(format_buff), &my_charset_bin), *format; - val= args[0]->val_str(&val_str); + val= args[0]->val_str(&val_string); format= args[1]->val_str(&format_str); if (args[0]->null_value || args[1]->null_value) goto null_date; null_value= 0; - bzero((char*) ltime, sizeof(ltime)); + bzero((char*) ltime, sizeof(*ltime)); date_time_format.format.str= (char*) format->ptr(); date_time_format.format.length= format->length(); if (extract_date_time(&date_time_format, val->ptr(), val->length(), - ltime, cached_timestamp_type, 0, "datetime")) + ltime, cached_timestamp_type, 0, "datetime") || + ((fuzzy_date & TIME_NO_ZERO_DATE) && + (ltime->year == 0 || ltime->month == 0 || ltime->day == 0))) goto null_date; if (cached_timestamp_type == MYSQL_TIMESTAMP_TIME && ltime->day) { @@ -3097,8 +3226,13 @@ String *Item_func_str_to_date::val_str(String *str) bool Item_func_last_day::get_date(TIME *ltime, uint fuzzy_date) { - if (get_arg0_date(ltime, fuzzy_date & ~TIME_FUZZY_DATE)) + if (get_arg0_date(ltime, fuzzy_date & ~TIME_FUZZY_DATE) || + (ltime->month == 0)) + { + null_value= 1; return 1; + } + null_value= 0; uint month_idx= ltime->month-1; ltime->day= days_in_month[month_idx]; if ( month_idx == 1 && calc_days_in_year(ltime->year) == 366) diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 2a6ddd1bc65..e53826ce3df 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -360,12 +359,20 @@ public: decimals=0; max_length=MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; } - int save_in_field(Field *to, bool no_conversions); Field *tmp_table_field(TABLE *table) { return tmp_table_field_from_field_type(table, 0); } bool result_as_longlong() { return TRUE; } + my_decimal *val_decimal(my_decimal *decimal_value) + { + DBUG_ASSERT(fixed == 1); + return val_decimal_from_date(decimal_value); + } + int save_in_field(Field *field, bool no_conversions) + { + return save_date_in_field(field); + } }; @@ -382,29 +389,61 @@ public: return tmp_table_field_from_field_type(table, 0); } bool result_as_longlong() { return TRUE; } + my_decimal *val_decimal(my_decimal *decimal_value) + { + DBUG_ASSERT(fixed == 1); + return val_decimal_from_date(decimal_value); + } + int save_in_field(Field *field, bool no_conversions) + { + return save_date_in_field(field); + } +}; + + +class Item_str_timefunc :public Item_str_func +{ +public: + Item_str_timefunc() :Item_str_func() {} + Item_str_timefunc(Item *a) :Item_str_func(a) {} + Item_str_timefunc(Item *a,Item *b) :Item_str_func(a,b) {} + Item_str_timefunc(Item *a, Item *b, Item *c) :Item_str_func(a, b ,c) {} + enum_field_types field_type() const { return MYSQL_TYPE_TIME; } + void fix_length_and_dec() + { + decimals=0; + max_length=MAX_TIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; + } + Field *tmp_table_field(TABLE *table) + { + return tmp_table_field_from_field_type(table, 0); + } + my_decimal *val_decimal(my_decimal *decimal_value) + { + DBUG_ASSERT(fixed == 1); + return val_decimal_from_time(decimal_value); + } + int save_in_field(Field *field, bool no_conversions) + { + return save_time_in_field(field); + } }; /* Abstract CURTIME function. Children should define what time zone is used */ -class Item_func_curtime :public Item_func +class Item_func_curtime :public Item_str_timefunc { longlong value; char buff[9*2+32]; uint buff_length; public: - Item_func_curtime() :Item_func() {} - Item_func_curtime(Item *a) :Item_func(a) {} - enum Item_result result_type () const { return STRING_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_TIME; } + Item_func_curtime() :Item_str_timefunc() {} + Item_func_curtime(Item *a) :Item_str_timefunc(a) {} double val_real() { DBUG_ASSERT(fixed == 1); return (double) value; } longlong val_int() { DBUG_ASSERT(fixed == 1); return value; } String *val_str(String *str); void fix_length_and_dec(); - Field *tmp_table_field(TABLE *table) - { - return tmp_table_field_from_field_type(table, 0); - } /* Abstract method that defines which time zone is used for conversion. Converts time current time in my_time_t representation to broken-down @@ -626,10 +665,10 @@ class Item_func_convert_tz :public Item_date_func }; -class Item_func_sec_to_time :public Item_str_func +class Item_func_sec_to_time :public Item_str_timefunc { public: - Item_func_sec_to_time(Item *item) :Item_str_func(item) {} + Item_func_sec_to_time(Item *item) :Item_str_timefunc(item) {} double val_real() { DBUG_ASSERT(fixed == 1); @@ -639,17 +678,12 @@ public: String *val_str(String *); void fix_length_and_dec() { + Item_str_timefunc::fix_length_and_dec(); collation.set(&my_charset_bin); maybe_null=1; decimals= DATETIME_DEC; - max_length=MAX_TIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; } - enum_field_types field_type() const { return MYSQL_TYPE_TIME; } const char *func_name() const { return "sec_to_time"; } - Field *tmp_table_field(TABLE *table) - { - return tmp_table_field_from_field_type(table, 0); - } bool result_as_longlong() { return TRUE; } bool check_partition_func_processor(byte *int_arg) {return FALSE;} }; @@ -774,6 +808,15 @@ public: } bool result_as_longlong() { return TRUE; } longlong val_int(); + my_decimal *val_decimal(my_decimal *decimal_value) + { + DBUG_ASSERT(fixed == 1); + return val_decimal_from_date(decimal_value); + } + int save_in_field(Field *field, bool no_conversions) + { + return save_date_in_field(field); + } }; @@ -792,6 +835,15 @@ public: } bool result_as_longlong() { return TRUE; } longlong val_int(); + my_decimal *val_decimal(my_decimal *decimal_value) + { + DBUG_ASSERT(fixed == 1); + return val_decimal_from_time(decimal_value); + } + int save_in_field(Field *field, bool no_conversions) + { + return save_time_in_field(field); + } }; @@ -809,12 +861,21 @@ public: } bool result_as_longlong() { return TRUE; } longlong val_int(); + my_decimal *val_decimal(my_decimal *decimal_value) + { + DBUG_ASSERT(fixed == 1); + return val_decimal_from_date(decimal_value); + } + int save_in_field(Field *field, bool no_conversions) + { + return save_date_in_field(field); + } }; -class Item_func_makedate :public Item_str_func +class Item_func_makedate :public Item_date_func { public: - Item_func_makedate(Item *a,Item *b) :Item_str_func(a,b) {} + Item_func_makedate(Item *a,Item *b) :Item_date_func(a,b) {} String *val_str(String *str); const char *func_name() const { return "makedate"; } enum_field_types field_type() const { return MYSQL_TYPE_DATE; } @@ -823,11 +884,6 @@ public: decimals=0; max_length=MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; } - Field *tmp_table_field(TABLE *table) - { - return tmp_table_field_from_field_type(table, 0); - } - bool result_as_longlong() { return TRUE; } longlong val_int(); bool check_partition_func_processor(byte *int_arg) {return FALSE;} }; @@ -853,45 +909,46 @@ public: void print(String *str); const char *func_name() const { return "add_time"; } bool check_partition_func_processor(byte *int_arg) {return FALSE;} + my_decimal *val_decimal(my_decimal *decimal_value) + { + DBUG_ASSERT(fixed == 1); + if (cached_field_type == MYSQL_TYPE_TIME) + return val_decimal_from_time(decimal_value); + if (cached_field_type == MYSQL_TYPE_DATETIME) + return val_decimal_from_date(decimal_value); + return Item_str_func::val_decimal(decimal_value); + } + int save_in_field(Field *field, bool no_conversions) + { + if (cached_field_type == MYSQL_TYPE_TIME) + return save_time_in_field(field); + if (cached_field_type == MYSQL_TYPE_DATETIME) + return save_date_in_field(field); + return Item_str_func::save_in_field(field, no_conversions); + } }; -class Item_func_timediff :public Item_str_func +class Item_func_timediff :public Item_str_timefunc { public: Item_func_timediff(Item *a, Item *b) - :Item_str_func(a, b) {} + :Item_str_timefunc(a, b) {} String *val_str(String *str); const char *func_name() const { return "timediff"; } - enum_field_types field_type() const { return MYSQL_TYPE_TIME; } void fix_length_and_dec() { - decimals=0; - max_length=MAX_TIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; + Item_str_timefunc::fix_length_and_dec(); maybe_null= 1; } - Field *tmp_table_field(TABLE *table) - { - return tmp_table_field_from_field_type(table, 0); - } }; -class Item_func_maketime :public Item_str_func +class Item_func_maketime :public Item_str_timefunc { public: Item_func_maketime(Item *a, Item *b, Item *c) - :Item_str_func(a, b ,c) {} + :Item_str_timefunc(a, b ,c) {} String *val_str(String *str); const char *func_name() const { return "maketime"; } - enum_field_types field_type() const { return MYSQL_TYPE_TIME; } - void fix_length_and_dec() - { - decimals=0; - max_length=MAX_TIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - } - Field *tmp_table_field(TABLE *table) - { - return tmp_table_field_from_field_type(table, 0); - } bool check_partition_func_processor(byte *int_arg) {return FALSE;} }; diff --git a/sql/item_uniq.cc b/sql/item_uniq.cc deleted file mode 100644 index 9db8228b345..00000000000 --- a/sql/item_uniq.cc +++ /dev/null @@ -1,32 +0,0 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -/* Compability file */ - -#ifdef USE_PRAGMA_IMPLEMENTATION -#pragma implementation // gcc: Class implementation -#endif - -#include "mysql_priv.h" - -Field *Item_sum_unique_users::create_tmp_field(bool group, TABLE *table, - uint convert_blob_length) -{ - Field *field= new Field_long(9, maybe_null, name, 1); - if (field) - field->init(table); - return field; -} diff --git a/sql/item_uniq.h b/sql/item_uniq.h deleted file mode 100644 index a0aa0b96cc6..00000000000 --- a/sql/item_uniq.h +++ /dev/null @@ -1,63 +0,0 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -/* Compability file ; This file only contains dummy functions */ - -#ifdef USE_PRAGMA_INTERFACE -#pragma interface -#endif - -#include <queues.h> - -class Item_func_unique_users :public Item_real_func -{ -public: - Item_func_unique_users(Item *name_arg,int start,int end,List<Item> &list) - :Item_real_func(list) {} - double val_real() { DBUG_ASSERT(fixed == 1); return 0.0; } - void fix_length_and_dec() { decimals=0; max_length=6; } - void print(String *str) { str->append(STRING_WITH_LEN("0.0")); } - const char *func_name() const { return "unique_users"; } -}; - - -class Item_sum_unique_users :public Item_sum_num -{ -public: - Item_sum_unique_users(Item *name_arg,int start,int end,Item *item_arg) - :Item_sum_num(item_arg) {} - Item_sum_unique_users(THD *thd, Item_sum_unique_users *item) - :Item_sum_num(thd, item) {} - double val_real() { DBUG_ASSERT(fixed == 1); return 0.0; } - enum Sumfunctype sum_func () const {return UNIQUE_USERS_FUNC;} - void clear() {} - bool add() { return 0; } - void reset_field() {} - void update_field() {} - bool fix_fields(THD *thd, Item **ref) - { - DBUG_ASSERT(fixed == 0); - fixed= 1; - return FALSE; - } - Item *copy_or_same(THD* thd) - { - return new Item_sum_unique_users(thd, this); - } - void print(String *str) { str->append(STRING_WITH_LEN("0.0")); } - Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length); - const char *func_name() const { return "sum_unique_users"; } -}; diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 21239a13735..26474990644 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2005-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -12,8 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __GNUC__ #pragma implementation @@ -579,7 +577,6 @@ String * Item_nodeset_func_union::val_nodeset(String *nodeset) both_str.alloc(numnodes); char *both= (char*) both_str.ptr(); bzero((void*)both, numnodes); - uint pos= 0; MY_XPATH_FLT *flt; fltbeg= (MY_XPATH_FLT*) s0->ptr(); @@ -777,7 +774,7 @@ String *Item_nodeset_func_elementbyindex::val_nodeset(String *nodeset) ((XPathFilter*)(&nodeset_func->context_cache))->append_element(flt->num, flt->pos, size); - int index= args[1]->val_int() - 1; + int index= (int) (args[1]->val_int()) - 1; if (index >= 0 && (flt->pos == (uint) index || args[1]->is_bool_func())) ((XPathFilter*)nodeset)->append_element(flt->num, pos++); } @@ -1047,12 +1044,12 @@ static struct my_xpath_keyword_names_st my_keyword_names[] = {MY_XPATH_LEX_OR , "or" , 2, 0 }, {MY_XPATH_LEX_DIV , "div" , 3, 0 }, {MY_XPATH_LEX_MOD , "mod" , 3, 0 }, - - {MY_XPATH_LEX_NODETYPE, "comment" , 7, 0 }, - {MY_XPATH_LEX_NODETYPE, "text" , 4, 0 }, - {MY_XPATH_LEX_NODETYPE, "processing-instruction" , 22,0 }, - {MY_XPATH_LEX_NODETYPE, "node" , 4, 0 }, - + {0,NULL,0,0} +}; + + +static struct my_xpath_keyword_names_st my_axis_names[]= +{ {MY_XPATH_LEX_AXIS,"ancestor" , 8,MY_XPATH_AXIS_ANCESTOR }, {MY_XPATH_LEX_AXIS,"ancestor-or-self" ,16,MY_XPATH_AXIS_ANCESTOR_OR_SELF }, {MY_XPATH_LEX_AXIS,"attribute" , 9,MY_XPATH_AXIS_ATTRIBUTE }, @@ -1066,7 +1063,16 @@ static struct my_xpath_keyword_names_st my_keyword_names[] = {MY_XPATH_LEX_AXIS,"preceding" , 9,MY_XPATH_AXIS_PRECEDING }, {MY_XPATH_LEX_AXIS,"preceding-sibling" ,17,MY_XPATH_AXIS_PRECEDING_SIBLING }, {MY_XPATH_LEX_AXIS,"self" , 4,MY_XPATH_AXIS_SELF }, + {0,NULL,0,0} +}; + +static struct my_xpath_keyword_names_st my_nodetype_names[]= +{ + {MY_XPATH_LEX_NODETYPE, "comment" , 7, 0 }, + {MY_XPATH_LEX_NODETYPE, "text" , 4, 0 }, + {MY_XPATH_LEX_NODETYPE, "processing-instruction" , 22,0 }, + {MY_XPATH_LEX_NODETYPE, "node" , 4, 0 }, {0,NULL,0,0} }; @@ -1081,11 +1087,14 @@ static struct my_xpath_keyword_names_st my_keyword_names[] = - Token type, on lookup success. - MY_XPATH_LEX_IDENT, on lookup failure. */ -static int my_xpath_keyword(MY_XPATH *x, const char *beg, const char *end) +static int +my_xpath_keyword(MY_XPATH *x, + struct my_xpath_keyword_names_st *keyword_names, + const char *beg, const char *end) { struct my_xpath_keyword_names_st *k; size_t length= end-beg; - for (k= my_keyword_names; k->name; k++) + for (k= keyword_names; k->name; k++) { if (length == k->length && !strncasecmp(beg, k->name, length)) { @@ -1371,15 +1380,32 @@ my_xpath_lex_scan(MY_XPATH *xpath, beg+= length) /* no op */; lex->end= beg; - // check if a function call - if (*beg == '(' && (xpath->func= my_xpath_function(lex->beg, beg))) + if (beg < end) { - lex->term= MY_XPATH_LEX_FUNC; - return; + if (*beg == '(') + { + /* + check if a function call, e.g.: count(/a/b) + or a nodetype test, e.g.: /a/b/text() + */ + if ((xpath->func= my_xpath_function(lex->beg, beg))) + lex->term= MY_XPATH_LEX_FUNC; + else + lex->term= my_xpath_keyword(xpath, my_nodetype_names, + lex->beg, beg); + return; + } + // check if an axis specifier, e.g.: /a/b/child::* + else if (*beg == ':' && beg + 1 < end && beg[1] == ':') + { + lex->term= my_xpath_keyword(xpath, my_axis_names, + lex->beg, beg); + return; + } } - // check if a keyword - lex->term= my_xpath_keyword(xpath, lex->beg, beg); + lex->term= my_xpath_keyword(xpath, my_keyword_names, + lex->beg, beg); return; } @@ -1486,7 +1512,6 @@ static int my_xpath_parse_AxisName(MY_XPATH *xpath) static int my_xpath_parse_LocationPath(MY_XPATH *xpath); static int my_xpath_parse_AbsoluteLocationPath(MY_XPATH *xpath); static int my_xpath_parse_RelativeLocationPath(MY_XPATH *xpath); -static int my_xpath_parse_AbbreviatedAbsoluteLocationPath(MY_XPATH *xpath); static int my_xpath_parse_AbbreviatedStep(MY_XPATH *xpath); static int my_xpath_parse_Step(MY_XPATH *xpath); static int my_xpath_parse_AxisSpecifier(MY_XPATH *xpath); @@ -1505,7 +1530,6 @@ static int my_xpath_parse_RelationalExpr(MY_XPATH *xpath); static int my_xpath_parse_AndExpr(MY_XPATH *xpath); static int my_xpath_parse_EqualityExpr(MY_XPATH *xpath); static int my_xpath_parse_VariableReference(MY_XPATH *xpath); -static int my_xpath_parse_slash_opt_slash(MY_XPATH *xpath); /* @@ -2334,6 +2358,36 @@ static int my_xpath_parse_Number(MY_XPATH *xpath) /* + Scan NCName. + + SYNOPSYS + + The keywords AND, OR, MOD, DIV are valid identitiers + when they are in identifier context: + + SELECT + ExtractValue('<and><or><mod><div>VALUE</div></mod></or></and>', + '/and/or/mod/div') + -> VALUE + + RETURN + 1 - success + 0 - failure +*/ + +static int +my_xpath_parse_NCName(MY_XPATH *xpath) +{ + return + my_xpath_parse_term(xpath, MY_XPATH_LEX_IDENT) || + my_xpath_parse_term(xpath, MY_XPATH_LEX_AND) || + my_xpath_parse_term(xpath, MY_XPATH_LEX_OR) || + my_xpath_parse_term(xpath, MY_XPATH_LEX_MOD) || + my_xpath_parse_term(xpath, MY_XPATH_LEX_DIV) ? 1 : 0; +} + + +/* QName grammar can be found in a separate document http://www.w3.org/TR/REC-xml-names/#NT-QName @@ -2341,16 +2395,17 @@ static int my_xpath_parse_Number(MY_XPATH *xpath) [7] Prefix ::= NCName [8] LocalPart ::= NCName */ + static int my_xpath_parse_QName(MY_XPATH *xpath) { const char *beg; - if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_IDENT)) + if (!my_xpath_parse_NCName(xpath)) return 0; beg= xpath->prevtok.beg; if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_COLON)) return 1; /* Non qualified name */ - if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_IDENT)) + if (!my_xpath_parse_NCName(xpath)) return 0; xpath->prevtok.beg= beg; return 1; @@ -2701,7 +2756,6 @@ String *Item_func_xml_update::val_str(String *str) } MY_XML_NODE *nodebeg= (MY_XML_NODE*) pxml.ptr(); - MY_XML_NODE *nodeend= (MY_XML_NODE*) pxml.ptr() + pxml.length(); MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) nodeset->ptr(); MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (nodeset->ptr() + nodeset->length()); diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h index 6fa33081373..9f2860ef403 100644 --- a/sql/item_xmlfunc.h +++ b/sql/item_xmlfunc.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/key.cc b/sql/key.cc index be21bf11c3c..bd614b10a70 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,37 +18,54 @@ #include "mysql_priv.h" - /* - ** Search after with key field is. If no key starts with field test - ** if field is part of some key. - ** - ** returns number of key. keylength is set to length of key before - ** (not including) field - ** Used when calculating key for NEXT_NUMBER - */ +/* + Search after a key that starts with 'field' + + SYNOPSIS + find_ref_key() + key First key to check + key_count How many keys to check + record Start of record + field Field to search after + key_length On partial match, contains length of fields before + field + + NOTES + Used when calculating key for NEXT_NUMBER -int find_ref_key(KEY *key, uint key_count, Field *field, uint *key_length) + IMPLEMENTATION + If no key starts with field test if field is part of some key. If we find + one, then return first key and set key_length to the number of bytes + preceding 'field'. + + RETURN + -1 field is not part of the key + # Key part for key matching key. + key_length is set to length of key before (not including) field +*/ + +int find_ref_key(KEY *key, uint key_count, byte *record, Field *field, + uint *key_length) { reg2 int i; reg3 KEY *key_info; uint fieldpos; - fieldpos= field->offset(); - - /* Test if some key starts as fieldpos */ + fieldpos= field->offset(record); + /* Test if some key starts as fieldpos */ for (i= 0, key_info= key ; i < (int) key_count ; i++, key_info++) { if (key_info->key_part[0].offset == fieldpos) - { /* Found key. Calc keylength */ + { /* Found key. Calc keylength */ *key_length=0; - return(i); /* Use this key */ + return(i); /* Use this key */ } } - /* Test if some key contains fieldpos */ + /* Test if some key contains fieldpos */ for (i= 0, key_info= key; i < (int) key_count ; i++, key_info++) @@ -62,7 +78,7 @@ int find_ref_key(KEY *key, uint key_count, Field *field, uint *key_length) j++, key_part++) { if (key_part->offset == fieldpos) - return(i); /* Use this key */ + return(i); /* Use this key */ *key_length+=key_part->store_length; } } diff --git a/sql/lex.h b/sql/lex.h index f19c9413e0e..45155da7692 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -30,21 +29,16 @@ SYM_GROUP sym_group_rtree= {"RTree keys", "HAVE_RTREE_KEYS"}; #define SYM_OR_NULL(A) A #endif -#define SYM(A) SYM_OR_NULL(A),0,0,&sym_group_common -#define F_SYM(A) SYM_OR_NULL(A) - -#define CREATE_FUNC(A) (void *)(SYM_OR_NULL(A)), &sym_group_common - -#ifdef HAVE_SPATIAL -#define CREATE_FUNC_GEOM(A) (void *)(SYM_OR_NULL(A)), &sym_group_geom -#else -#define CREATE_FUNC_GEOM(A) 0, &sym_group_geom -#endif +#define SYM(A) SYM_OR_NULL(A),0,&sym_group_common /* Symbols are broken into separated arrays to allow field names with same name as functions. These are kept sorted for human lookup (the symbols are hashed). + + NOTE! The symbol tables should be the same regardless of what features + are compiled into the server. Don't add ifdef'ed symbols to the + lists */ static SYMBOL symbols[] = { @@ -228,12 +222,13 @@ static SYMBOL symbols[] = { { "GLOBAL", SYM(GLOBAL_SYM)}, { "GRANT", SYM(GRANT)}, { "GRANTS", SYM(GRANTS)}, - { "GROUP", SYM(GROUP)}, + { "GROUP", SYM(GROUP_SYM)}, { "HANDLER", SYM(HANDLER_SYM)}, { "HASH", SYM(HASH_SYM)}, { "HAVING", SYM(HAVING)}, { "HELP", SYM(HELP_SYM)}, { "HIGH_PRIORITY", SYM(HIGH_PRIORITY)}, + { "HOST", SYM(HOST_SYM)}, { "HOSTS", SYM(HOSTS_SYM)}, { "HOUR", SYM(HOUR_SYM)}, { "HOUR_MICROSECOND", SYM(HOUR_MICROSECOND_SYM)}, @@ -373,6 +368,7 @@ static SYMBOL symbols[] = { { "ONE_SHOT", SYM(ONE_SHOT_SYM)}, { "OPEN", SYM(OPEN_SYM)}, { "OPTIMIZE", SYM(OPTIMIZE)}, + { "OPTIONS", SYM(OPTIONS_SYM)}, { "OPTION", SYM(OPTION)}, { "OPTIONALLY", SYM(OPTIONALLY)}, { "OR", SYM(OR_SYM)}, @@ -380,20 +376,20 @@ static SYMBOL symbols[] = { { "OUT", SYM(OUT_SYM)}, { "OUTER", SYM(OUTER)}, { "OUTFILE", SYM(OUTFILE)}, + { "OWNER", SYM(OWNER_SYM)}, { "PACK_KEYS", SYM(PACK_KEYS_SYM)}, { "PARSER", SYM(PARSER_SYM)}, { "PARTIAL", SYM(PARTIAL)}, -#ifdef WITH_PARTITION_STORAGE_ENGINE { "PARTITION", SYM(PARTITION_SYM)}, { "PARTITIONING", SYM(PARTITIONING_SYM)}, { "PARTITIONS", SYM(PARTITIONS_SYM)}, -#endif { "PASSWORD", SYM(PASSWORD)}, { "PHASE", SYM(PHASE_SYM)}, { "PLUGIN", SYM(PLUGIN_SYM)}, { "PLUGINS", SYM(PLUGINS_SYM)}, { "POINT", SYM(POINT_SYM)}, { "POLYGON", SYM(POLYGON)}, + { "PORT", SYM(PORT_SYM)}, { "PRECISION", SYM(PRECISION)}, { "PREPARE", SYM(PREPARE_SYM)}, { "PRESERVE", SYM(PRESERVE_SYM)}, @@ -463,6 +459,7 @@ static SYMBOL symbols[] = { { "SERIAL", SYM(SERIAL_SYM)}, { "SERIALIZABLE", SYM(SERIALIZABLE_SYM)}, { "SESSION", SYM(SESSION_SYM)}, + { "SERVER", SYM(SERVER_SYM)}, { "SET", SYM(SET)}, { "SHARE", SYM(SHARE_SYM)}, { "SHOW", SYM(SHOW)}, @@ -472,6 +469,7 @@ static SYMBOL symbols[] = { { "SLAVE", SYM(SLAVE)}, { "SNAPSHOT", SYM(SNAPSHOT_SYM)}, { "SMALLINT", SYM(SMALLINT)}, + { "SOCKET", SYM(SOCKET_SYM)}, { "SOME", SYM(ANY_SYM)}, { "SONAME", SYM(SONAME_SYM)}, { "SOUNDS", SYM(SOUNDS_SYM)}, @@ -576,6 +574,7 @@ static SYMBOL symbols[] = { { "VIEW", SYM(VIEW_SYM)}, { "WITH", SYM(WITH)}, { "WORK", SYM(WORK_SYM)}, + { "WRAPPER", SYM(WRAPPER_SYM)}, { "WRITE", SYM(WRITE_SYM)}, { "X509", SYM(X509_SYM)}, { "XOR", SYM(XOR)}, @@ -588,235 +587,36 @@ static SYMBOL symbols[] = { static SYMBOL sql_functions[] = { - { "ABS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_abs)}, - { "ACOS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_acos)}, { "ADDDATE", SYM(ADDDATE_SYM)}, - { "ADDTIME", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_addtime)}, - { "AES_ENCRYPT", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_aes_encrypt)}, - { "AES_DECRYPT", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_aes_decrypt)}, - { "AREA", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_area)}, - { "ASIN", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_asin)}, - { "ASBINARY", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_as_wkb)}, - { "ASTEXT", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_as_wkt)}, - { "ASWKB", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_as_wkb)}, - { "ASWKT", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_as_wkt)}, - { "ATAN", SYM(ATAN)}, - { "ATAN2", SYM(ATAN)}, - { "BENCHMARK", SYM(BENCHMARK_SYM)}, - { "BIN", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_bin)}, - { "BIT_COUNT", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_bit_count)}, - { "BIT_OR", SYM(BIT_OR)}, { "BIT_AND", SYM(BIT_AND)}, + { "BIT_OR", SYM(BIT_OR)}, { "BIT_XOR", SYM(BIT_XOR)}, { "CAST", SYM(CAST_SYM)}, - { "CEIL", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ceiling)}, - { "CEILING", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ceiling)}, - { "BIT_LENGTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_bit_length)}, - { "CENTROID", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_centroid)}, - { "CHAR_LENGTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_char_length)}, - { "CHARACTER_LENGTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_char_length)}, - { "COERCIBILITY", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_coercibility)}, - { "COMPRESS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_compress)}, - { "CONCAT", SYM(CONCAT)}, - { "CONCAT_WS", SYM(CONCAT_WS)}, - { "CONNECTION_ID", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_connection_id)}, - { "CONV", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_conv)}, - { "CONVERT_TZ", SYM(CONVERT_TZ_SYM)}, { "COUNT", SYM(COUNT_SYM)}, - { "COS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cos)}, - { "COT", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cot)}, - { "CRC32", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_crc32)}, - { "CROSSES", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_crosses)}, { "CURDATE", SYM(CURDATE)}, { "CURTIME", SYM(CURTIME)}, { "DATE_ADD", SYM(DATE_ADD_INTERVAL)}, - { "DATEDIFF", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_datediff)}, - { "DATE_FORMAT", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_date_format)}, { "DATE_SUB", SYM(DATE_SUB_INTERVAL)}, - { "DAYNAME", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_dayname)}, - { "DAYOFMONTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_dayofmonth)}, - { "DAYOFWEEK", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_dayofweek)}, - { "DAYOFYEAR", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_dayofyear)}, - { "DECODE", SYM(DECODE_SYM)}, - { "DEGREES", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_degrees)}, - { "DES_ENCRYPT", SYM(DES_ENCRYPT_SYM)}, - { "DES_DECRYPT", SYM(DES_DECRYPT_SYM)}, - { "DIMENSION", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_dimension)}, - { "DISJOINT", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_disjoint)}, - { "ELT", SYM(ELT_FUNC)}, - { "ENCODE", SYM(ENCODE_SYM)}, - { "ENCRYPT", SYM(ENCRYPT)}, - { "ENDPOINT", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_endpoint)}, - { "ENVELOPE", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_envelope)}, - { "EQUALS", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_equals)}, - { "EXTERIORRING", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_exteriorring)}, { "EXTRACT", SYM(EXTRACT_SYM)}, - { "EXTRACTVALUE", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_xml_extractvalue)}, - { "EXP", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_exp)}, - { "EXPORT_SET", SYM(EXPORT_SET)}, - { "FIELD", SYM(FIELD_FUNC)}, /* For compability */ - { "FIND_IN_SET", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_find_in_set)}, - { "FLOOR", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_floor)}, - { "FORMAT", SYM(FORMAT_SYM)}, - { "FOUND_ROWS", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_found_rows)}, - { "FROM_DAYS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_from_days)}, - { "FROM_UNIXTIME", SYM(FROM_UNIXTIME)}, - { "GET_LOCK", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_get_lock)}, - { "GEOMETRYN", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_geometryn)}, - { "GEOMETRYTYPE", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_geometry_type)}, - { "GEOMCOLLFROMTEXT", SYM(GEOMCOLLFROMTEXT)}, - { "GEOMCOLLFROMWKB", SYM(GEOMFROMWKB)}, - { "GEOMETRYCOLLECTIONFROMTEXT",SYM(GEOMCOLLFROMTEXT)}, - { "GEOMETRYCOLLECTIONFROMWKB",SYM(GEOMFROMWKB)}, - { "GEOMETRYFROMTEXT", SYM(GEOMFROMTEXT)}, - { "GEOMETRYFROMWKB", SYM(GEOMFROMWKB)}, - { "GEOMFROMTEXT", SYM(GEOMFROMTEXT)}, - { "GEOMFROMWKB", SYM(GEOMFROMWKB)}, - { "GLENGTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_glength)}, - { "GREATEST", SYM(GREATEST_SYM)}, { "GROUP_CONCAT", SYM(GROUP_CONCAT_SYM)}, - { "GROUP_UNIQUE_USERS", SYM(GROUP_UNIQUE_USERS)}, - { "HEX", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_hex)}, - { "IFNULL", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_ifnull)}, - { "INET_ATON", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_inet_aton)}, - { "INET_NTOA", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_inet_ntoa)}, - { "INSTR", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_instr)}, - { "INTERIORRINGN", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_interiorringn)}, - { "INTERSECTS", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_intersects)}, - { "ISCLOSED", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_isclosed)}, - { "ISEMPTY", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_isempty)}, - { "ISNULL", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_isnull)}, - { "IS_FREE_LOCK", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_is_free_lock)}, - { "IS_USED_LOCK", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_is_used_lock)}, - { "LAST_INSERT_ID", SYM(LAST_INSERT_ID)}, - { "ISSIMPLE", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_issimple)}, - { "LAST_DAY", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_last_day)}, - { "LCASE", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_lcase)}, - { "LEAST", SYM(LEAST_SYM)}, - { "LENGTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_length)}, - { "LN", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ln)}, - { "LINEFROMTEXT", SYM(LINEFROMTEXT)}, - { "LINEFROMWKB", SYM(GEOMFROMWKB)}, - { "LINESTRINGFROMTEXT",SYM(LINEFROMTEXT)}, - { "LINESTRINGFROMWKB",SYM(GEOMFROMWKB)}, - { "LOAD_FILE", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_load_file)}, - { "LOCATE", SYM(LOCATE)}, - { "LOG", SYM(LOG_SYM)}, - { "LOG2", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_log2)}, - { "LOG10", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_log10)}, - { "LOWER", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_lcase)}, - { "LPAD", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_lpad)}, - { "LTRIM", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ltrim)}, - { "MAKE_SET", SYM(MAKE_SET_SYM)}, - { "MAKEDATE", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_makedate)}, - { "MAKETIME", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_maketime)}, - { "MASTER_POS_WAIT", SYM(MASTER_POS_WAIT)}, { "MAX", SYM(MAX_SYM)}, - { "MBRCONTAINS", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_contains)}, - { "MBRDISJOINT", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_disjoint)}, - { "MBREQUAL", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_equals)}, - { "MBRINTERSECTS", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_intersects)}, - { "MBROVERLAPS", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_overlaps)}, - { "MBRTOUCHES", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_touches)}, - { "MBRWITHIN", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_within)}, - { "MD5", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_md5)}, { "MID", SYM(SUBSTRING)}, /* unireg function */ { "MIN", SYM(MIN_SYM)}, - { "MLINEFROMTEXT", SYM(MLINEFROMTEXT)}, - { "MLINEFROMWKB", SYM(GEOMFROMWKB)}, - { "MPOINTFROMTEXT", SYM(MPOINTFROMTEXT)}, - { "MPOINTFROMWKB", SYM(GEOMFROMWKB)}, - { "MPOLYFROMTEXT", SYM(MPOLYFROMTEXT)}, - { "MPOLYFROMWKB", SYM(GEOMFROMWKB)}, - { "MONTHNAME", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_monthname)}, - { "MULTILINESTRINGFROMTEXT",SYM(MLINEFROMTEXT)}, - { "MULTILINESTRINGFROMWKB",SYM(GEOMFROMWKB)}, - { "MULTIPOINTFROMTEXT",SYM(MPOINTFROMTEXT)}, - { "MULTIPOINTFROMWKB",SYM(GEOMFROMWKB)}, - { "MULTIPOLYGONFROMTEXT",SYM(MPOLYFROMTEXT)}, - { "MULTIPOLYGONFROMWKB",SYM(GEOMFROMWKB)}, - { "NAME_CONST", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_name_const)}, { "NOW", SYM(NOW_SYM)}, - { "NULLIF", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_nullif)}, - { "NUMGEOMETRIES", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_numgeometries)}, - { "NUMINTERIORRINGS", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_numinteriorring)}, - { "NUMPOINTS", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_numpoints)}, - { "OCTET_LENGTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_length)}, - { "OCT", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_oct)}, - { "ORD", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ord)}, - { "OVERLAPS", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_overlaps)}, - { "PERIOD_ADD", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_period_add)}, - { "PERIOD_DIFF", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_period_diff)}, - { "PI", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_pi)}, - { "POINTFROMTEXT", SYM(POINTFROMTEXT)}, - { "POINTFROMWKB", SYM(GEOMFROMWKB)}, - { "POINTN", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_pointn)}, - { "POLYFROMTEXT", SYM(POLYFROMTEXT)}, - { "POLYFROMWKB", SYM(GEOMFROMWKB)}, - { "POLYGONFROMTEXT", SYM(POLYFROMTEXT)}, - { "POLYGONFROMWKB", SYM(GEOMFROMWKB)}, { "POSITION", SYM(POSITION_SYM)}, - { "POW", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_pow)}, - { "POWER", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_pow)}, - { "QUOTE", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_quote)}, - { "RADIANS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_radians)}, - { "RAND", SYM(RAND)}, - { "RELEASE_LOCK", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_release_lock)}, - { "REVERSE", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_reverse)}, - { "ROUND", SYM(ROUND)}, - { "ROW_COUNT", SYM(ROW_COUNT_SYM)}, - { "RPAD", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_rpad)}, - { "RTRIM", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_rtrim)}, - { "SEC_TO_TIME", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sec_to_time)}, - { "SESSION_USER", SYM(USER)}, - { "SUBDATE", SYM(SUBDATE_SYM)}, - { "SIGN", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sign)}, - { "SIN", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sin)}, - { "SHA", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sha)}, - { "SHA1", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sha)}, - { "SLEEP", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sleep)}, - { "SOUNDEX", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_soundex)}, - { "SPACE", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_space)}, - { "SQRT", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sqrt)}, - { "SRID", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_srid)}, - { "STARTPOINT", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_startpoint)}, + { "SESSION_USER", SYM(USER)}, { "STD", SYM(STD_SYM)}, { "STDDEV", SYM(STD_SYM)}, { "STDDEV_POP", SYM(STD_SYM)}, { "STDDEV_SAMP", SYM(STDDEV_SAMP_SYM)}, - { "STR_TO_DATE", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_str_to_date)}, - { "STRCMP", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_strcmp)}, + { "SUBDATE", SYM(SUBDATE_SYM)}, { "SUBSTR", SYM(SUBSTRING)}, { "SUBSTRING", SYM(SUBSTRING)}, - { "SUBSTRING_INDEX", SYM(SUBSTRING_INDEX)}, - { "SUBTIME", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_subtime)}, { "SUM", SYM(SUM_SYM)}, { "SYSDATE", SYM(SYSDATE)}, - { "SYSTEM_USER", SYM(USER)}, - { "TAN", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_tan)}, - { "TIME_FORMAT", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_time_format)}, - { "TIME_TO_SEC", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_time_to_sec)}, - { "TIMEDIFF", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_timediff)}, - { "TO_DAYS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_to_days)}, - { "TOUCHES", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_touches)}, + { "SYSTEM_USER", SYM(USER)}, { "TRIM", SYM(TRIM)}, - { "UCASE", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ucase)}, - { "UNCOMPRESS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_uncompress)}, - { "UNCOMPRESSED_LENGTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_uncompressed_length)}, - { "UNHEX", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_unhex)}, - { "UNIQUE_USERS", SYM(UNIQUE_USERS)}, - { "UNIX_TIMESTAMP", SYM(UNIX_TIMESTAMP)}, - { "UPDATEXML", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_xml_update)}, - { "UPPER", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ucase)}, - { "UUID", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_uuid)}, { "VARIANCE", SYM(VARIANCE_SYM)}, { "VAR_POP", SYM(VARIANCE_SYM)}, { "VAR_SAMP", SYM(VAR_SAMP_SYM)}, - { "VERSION", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_version)}, - { "WEEKDAY", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_weekday)}, - { "WEEKOFYEAR", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_weekofyear)}, - { "WITHIN", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_within)}, - { "X", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_x)}, - { "Y", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_y)}, - { "YEARWEEK", SYM(YEARWEEK)} }; diff --git a/sql/lex_symbol.h b/sql/lex_symbol.h index 3074a489b6a..000c0709071 100644 --- a/sql/lex_symbol.h +++ b/sql/lex_symbol.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2001, 2004 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -26,7 +25,6 @@ typedef struct st_symbol { const char *name; uint tok; uint length; - void *create_func; struct st_sym_group *group; } SYMBOL; diff --git a/sql/lock.cc b/sql/lock.cc index 06f538a2a03..a93033bfdd0 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -151,6 +150,23 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, } } + if ( write_lock_used + && opt_readonly + && ! (thd->security_ctx->master_access & SUPER_ACL) + && ! thd->slave_thread + ) + { + /* + Someone has issued SET GLOBAL READ_ONLY=1 and we want a write lock. + We do not wait for READ_ONLY=0, and fail. + */ + reset_lock_data(sql_lock); + my_free((gptr) sql_lock, MYF(0)); + sql_lock=0; + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); + break; + } + thd->proc_info="System lock"; DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info)); if (lock_external(thd, tables, count)) @@ -469,7 +485,7 @@ bool mysql_lock_abort_for_thread(THD *thd, TABLE *table) for (uint i=0; i < locked->lock_count; i++) { if (thr_abort_locks_for_thread(locked->locks[i]->lock, - table->in_use->real_id)) + table->in_use->thread_id)) result= TRUE; } my_free((gptr) locked,MYF(0)); @@ -588,7 +604,7 @@ TABLE_LIST *mysql_lock_have_duplicate(THD *thd, TABLE_LIST *needle, for (; haystack; haystack= haystack->next_global) { - if (haystack->placeholder() || haystack->schema_table) + if (haystack->placeholder()) continue; table2= haystack->table; if (table2->s->tmp_table == TMP_TABLE) @@ -691,7 +707,8 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, check_if_locking_is_allowed(thd->lex->sql_command, thd->lex->type, table_ptr[i], count, (thd == logger.get_general_log_thd()) || - (thd == logger.get_slow_log_thd()))) + (thd == logger.get_slow_log_thd()) || + (thd == logger.get_privileged_thread()))) DBUG_RETURN(0); } diff --git a/sql/log.cc b/sql/log.cc index 1cf89fad042..7e60950977a 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -32,15 +31,6 @@ #include <mysql/plugin.h> -/* - Define placement versions of operator new and operator delete since - we cannot be sure that the <new> include exists. - */ -inline void *operator new(size_t, void *ptr) { return ptr; } -inline void *operator new[](size_t, void *ptr) { return ptr; } -inline void operator delete(void*, void*) { /* Do nothing */ } -inline void operator delete[](void*, void*) { /* Do nothing */ } - /* max size of the log message */ #define MAX_LOG_BUFFER_SIZE 1024 #define MAX_USER_HOST_SIZE 512 @@ -76,29 +66,60 @@ sql_print_message_func sql_print_message_handlers[3] = char *make_default_log_name(char *buff,const char* log_ext) { - strmake(buff, glob_hostname, FN_REFLEN-5); + strmake(buff, pidfile_name, FN_REFLEN-5); return fn_format(buff, buff, mysql_data_home, log_ext, MYF(MY_UNPACK_FILENAME|MY_APPEND_EXT)); } /* + Helper class to hold a mutex for the duration of the + block. + + Eliminates the need for explicit unlocking of mutexes on, e.g., + error returns. On passing a null pointer, the sentry will not do + anything. + */ +class Mutex_sentry +{ +public: + Mutex_sentry(pthread_mutex_t *mutex) + : m_mutex(mutex) + { + if (m_mutex) + pthread_mutex_lock(mutex); + } + + ~Mutex_sentry() + { + if (m_mutex) + pthread_mutex_unlock(m_mutex); +#ifndef DBUG_OFF + m_mutex= 0; +#endif + } + +private: + pthread_mutex_t *m_mutex; + + // It's not allowed to copy this object in any way + Mutex_sentry(Mutex_sentry const&); + void operator=(Mutex_sentry const&); +}; + +/* Helper class to store binary log transaction data. */ class binlog_trx_data { public: binlog_trx_data() -#ifdef HAVE_ROW_BASED_REPLICATION : m_pending(0), before_stmt_pos(MY_OFF_T_UNDEF) -#endif { trans_log.end_of_file= max_binlog_cache_size; } ~binlog_trx_data() { -#ifdef HAVE_ROW_BASED_REPLICATION DBUG_ASSERT(pending() == NULL); -#endif close_cached_file(&trans_log); } @@ -108,11 +129,7 @@ public: bool empty() const { -#ifdef HAVE_ROW_BASED_REPLICATION return pending() == NULL && my_b_tell(&trans_log) == 0; -#else - return my_b_tell(&trans_log) == 0; -#endif } /* @@ -121,11 +138,13 @@ public: */ void truncate(my_off_t pos) { -#ifdef HAVE_ROW_BASED_REPLICATION + DBUG_PRINT("info", ("truncating to position %lu", (ulong) pos)); + DBUG_PRINT("info", ("before_stmt_pos=%lu", (ulong) pos)); delete pending(); set_pending(0); -#endif reinit_io_cache(&trans_log, WRITE_CACHE, pos, 0, 0); + if (pos < before_stmt_pos) + before_stmt_pos= MY_OFF_T_UNDEF; } /* @@ -135,13 +154,10 @@ public: void reset() { if (!empty()) truncate(0); -#ifdef HAVE_ROW_BASED_REPLICATION before_stmt_pos= MY_OFF_T_UNDEF; -#endif trans_log.end_of_file= max_binlog_cache_size; } -#ifdef HAVE_ROW_BASED_REPLICATION Rows_log_event *pending() const { return m_pending; @@ -151,12 +167,10 @@ public: { m_pending= pending; } -#endif IO_CACHE trans_log; // The transaction cache private: -#ifdef HAVE_ROW_BASED_REPLICATION /* Pending binrows event. This event is the event where the rows are currently written. @@ -168,11 +182,37 @@ public: Binlog position before the start of the current statement. */ my_off_t before_stmt_pos; -#endif }; handlerton *binlog_hton; + +/* Check if a given table is opened log table */ +int check_if_log_table(uint db_len, const char *db, uint table_name_len, + const char *table_name, uint check_if_opened) +{ + if (db_len == 5 && + !(lower_case_table_names ? + my_strcasecmp(system_charset_info, db, "mysql") : + strcmp(db, "mysql"))) + { + if (table_name_len == 11 && !(lower_case_table_names ? + my_strcasecmp(system_charset_info, + table_name, "general_log") : + strcmp(table_name, "general_log")) && + (!check_if_opened || logger.is_log_table_enabled(QUERY_LOG_GENERAL))) + return QUERY_LOG_GENERAL; + else + if (table_name_len == 8 && !(lower_case_table_names ? + my_strcasecmp(system_charset_info, table_name, "slow_log") : + strcmp(table_name, "slow_log")) && + (!check_if_opened ||logger.is_log_table_enabled(QUERY_LOG_SLOW))) + return QUERY_LOG_SLOW; + } + return 0; +} + + /* Open log table of a given type (general or slow log) @@ -219,7 +259,7 @@ bool Log_to_csv_event_handler::open_log_table(uint log_table_type) table->table_name_length= 8; break; default: - DBUG_ASSERT(0); + assert(0); // Impossible } /* @@ -263,6 +303,7 @@ bool Log_to_csv_event_handler::open_log_table(uint log_table_type) { table->table->use_all_columns(); table->table->locked_by_logger= TRUE; + table->table->no_replicate= TRUE; } /* restore thread settings */ if (curr) @@ -273,6 +314,12 @@ bool Log_to_csv_event_handler::open_log_table(uint log_table_type) my_pthread_setspecific_ptr(THR_MALLOC, 0); } + /* + After a log table was opened, we should clear privileged thread + flag (which allows locking of a log table by a special thread, usually + the one who closed log tables temporarily). + */ + privileged_thread= 0; DBUG_RETURN(error); } @@ -284,11 +331,15 @@ Log_to_csv_event_handler::Log_to_csv_event_handler() /* logger thread always works with mysql database */ general_log_thd->db= my_strdup("mysql", MYF(0)); general_log_thd->db_length= 5; + general_log.table= 0; slow_log_thd= new THD; /* logger thread always works with mysql database */ slow_log_thd->db= my_strdup("mysql", MYF(0));; slow_log_thd->db_length= 5; + slow_log.table= 0; + /* no privileged thread exists at the moment */ + privileged_thread= 0; } @@ -341,6 +392,7 @@ bool Log_to_csv_event_handler::reopen_log_table(uint log_table_type) return open_log_table(log_table_type); } + void Log_to_csv_event_handler::cleanup() { if (opt_log) @@ -407,9 +459,6 @@ bool Log_to_csv_event_handler:: filled by the Logger (=> no need to load default ones). */ - /* log table entries are not replicated at the moment */ - tmp_disable_binlog(current_thd); - /* Set current time. Required for CURRENT_TIMESTAMP to work */ general_log_thd->start_time= event_time; @@ -418,21 +467,36 @@ bool Log_to_csv_event_handler:: default value (which is CURRENT_TIMESTAMP). */ - table->field[1]->store(user_host, user_host_len, client_cs); + /* check that all columns exist */ + if (!table->field[1] || !table->field[2] || !table->field[3] || + !table->field[4] || !table->field[5]) + goto err; + + /* do a write */ + if (table->field[1]->store(user_host, user_host_len, client_cs) || + table->field[2]->store((longlong) thread_id, TRUE) || + table->field[3]->store((longlong) server_id, TRUE) || + table->field[4]->store(command_type, command_type_len, client_cs) || + table->field[5]->store(sql_text, sql_text_len, client_cs)) + goto err; + + /* mark tables as not null */ table->field[1]->set_notnull(); - table->field[2]->store((longlong) thread_id, TRUE); table->field[2]->set_notnull(); - table->field[3]->store((longlong) server_id, TRUE); table->field[3]->set_notnull(); - table->field[4]->store(command_type, command_type_len, client_cs); table->field[4]->set_notnull(); - table->field[5]->store(sql_text, sql_text_len, client_cs); table->field[5]->set_notnull(); + + /* log table entries are not replicated at the moment */ + tmp_disable_binlog(current_thd); + table->file->ha_write_row(table->record[0]); reenable_binlog(current_thd); return FALSE; +err: + return TRUE; } @@ -481,9 +545,6 @@ bool Log_to_csv_event_handler:: if (unlikely(!logger.is_log_tables_initialized)) return FALSE; - /* log table entries are not replicated at the moment */ - tmp_disable_binlog(current_thd); - /* Set start time for CURRENT_TIMESTAMP to the start of the query. This will be default value for the field[0] @@ -496,19 +557,30 @@ bool Log_to_csv_event_handler:: default value. */ + if (!table->field[1] || !table->field[2] || !table->field[3] || + !table->field[4] || !table->field[5] || !table->field[6] || + !table->field[7] || !table->field[8] || !table->field[9] || + !table->field[10]) + goto err; + /* store the value */ - table->field[1]->store(user_host, user_host_len, client_cs); + if (table->field[1]->store(user_host, user_host_len, client_cs)) + goto err; if (query_start_arg) { /* fill in query_time field */ - table->field[2]->store(query_time, TRUE); + if (table->field[2]->store(query_time, TRUE)) + goto err; /* lock_time */ - table->field[3]->store(lock_time, TRUE); + if (table->field[3]->store(lock_time, TRUE)) + goto err; /* rows_sent */ - table->field[4]->store((longlong) thd->sent_row_count, TRUE); + if (table->field[4]->store((longlong) thd->sent_row_count, TRUE)) + goto err; /* rows_examined */ - table->field[5]->store((longlong) thd->examined_row_count, TRUE); + if (table->field[5]->store((longlong) thd->examined_row_count, TRUE)) + goto err; } else { @@ -521,14 +593,18 @@ bool Log_to_csv_event_handler:: /* fill database field */ if (thd->db) { - table->field[6]->store(thd->db, thd->db_length, client_cs); + if (table->field[6]->store(thd->db, thd->db_length, client_cs)) + goto err; table->field[6]->set_notnull(); } if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt) { - table->field[7]->store((longlong) - thd->first_successful_insert_id_in_prev_stmt_for_binlog, TRUE); + if (table-> + field[7]->store((longlong) + thd->first_successful_insert_id_in_prev_stmt_for_binlog, + TRUE)) + goto err; table->field[7]->set_notnull(); } @@ -540,16 +616,23 @@ bool Log_to_csv_event_handler:: */ if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0) { - table->field[8]->store((longlong) - thd->auto_inc_intervals_in_cur_stmt_for_binlog.minimum(), TRUE); + if (table-> + field[8]->store((longlong) + thd->auto_inc_intervals_in_cur_stmt_for_binlog.minimum(), TRUE)) + goto err; table->field[8]->set_notnull(); } - table->field[9]->store((longlong) server_id, TRUE); + if (table->field[9]->store((longlong) server_id, TRUE)) + goto err; table->field[9]->set_notnull(); /* sql_text */ - table->field[10]->store(sql_text,sql_text_len, client_cs); + if (table->field[10]->store(sql_text,sql_text_len, client_cs)) + goto err; + + /* log table entries are not replicated at the moment */ + tmp_disable_binlog(current_thd); /* write the row */ table->file->ha_write_row(table->record[0]); @@ -557,6 +640,8 @@ bool Log_to_csv_event_handler:: reenable_binlog(current_thd); DBUG_RETURN(0); +err: + DBUG_RETURN(1); } bool Log_to_csv_event_handler:: @@ -745,61 +830,48 @@ bool LOGGER::reopen_log_table(uint log_table_type) return table_log_handler->reopen_log_table(log_table_type); } - -bool LOGGER::flush_logs(THD *thd) +bool LOGGER::reopen_log_tables() { - TABLE_LIST close_slow_log, close_general_log; + /* + we use | and not || here, to ensure that both reopen_log_table + are called, even if the first one fails + */ + if ((opt_slow_log && logger.reopen_log_table(QUERY_LOG_SLOW)) | + (opt_log && logger.reopen_log_table(QUERY_LOG_GENERAL))) + return TRUE; + return FALSE; +} - /* reopen log tables */ - bzero((char*) &close_slow_log, sizeof(TABLE_LIST)); - close_slow_log.alias= close_slow_log.table_name=(char*) "slow_log"; - close_slow_log.table_name_length= 8; - close_slow_log.db= (char*) "mysql"; - close_slow_log.db_length= 5; - bzero((char*) &close_general_log, sizeof(TABLE_LIST)); - close_general_log.alias= close_general_log.table_name=(char*) "general_log"; - close_general_log.table_name_length= 11; - close_general_log.db= (char*) "mysql"; - close_general_log.db_length= 5; +void LOGGER::tmp_close_log_tables(THD *thd) +{ + table_log_handler->tmp_close_log_tables(thd); +} - /* lock tables, in the case they are enabled */ - if (logger.is_log_tables_initialized) - { - /* - This will lock and wait for all but the logger thread to release the - tables. Then we could reopen log tables. Then release the name locks. - - NOTE: in fact, the first parameter used in lock_and_wait_for_table_name() - and table_log_handler->flush() could be any non-NULL THD, as the - underlying code makes certain assumptions about this. - Here we use one of the logger handler THD's. Simply because it - seems appropriate. - */ - if (opt_slow_log) - lock_and_wait_for_table_name(table_log_handler->general_log_thd, - &close_slow_log); - if (opt_log) - lock_and_wait_for_table_name(table_log_handler->general_log_thd, - &close_general_log); - } +bool LOGGER::flush_logs(THD *thd) +{ + int rc= 0; /* - Deny others from logging to general and slow log, - while reopening tables. + Now we lock logger, as nobody should be able to use logging routines while + log tables are closed */ logger.lock(); + if (logger.is_log_tables_initialized) + table_log_handler->tmp_close_log_tables(thd); // the locking happens here /* reopen log files */ file_log_handler->flush(); - /* flush tables, in the case they are enabled */ + /* reopen tables in the case they were enabled */ if (logger.is_log_tables_initialized) - table_log_handler->flush(table_log_handler->general_log_thd, - &close_slow_log, &close_general_log); + { + if (reopen_log_tables()) + rc= TRUE; + } /* end of log flush */ logger.unlock(); - return FALSE; + return rc; } @@ -827,9 +899,9 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length, bool is_command= FALSE; char user_host_buff[MAX_USER_HOST_SIZE]; - my_time_t current_time; + time_t current_time; Security_context *sctx= thd->security_ctx; - uint message_buff_len= 0, user_host_len= 0; + uint user_host_len= 0; longlong query_time= 0, lock_time= 0; /* @@ -1082,7 +1154,7 @@ void LOGGER::deactivate_log_handler(THD *thd, uint log_type) log_thd= table_log_handler->general_log_thd; break; default: - DBUG_ASSERT(0); + assert(0); // Impossible } if (!(*tmp_opt)) @@ -1107,31 +1179,50 @@ void LOGGER::deactivate_log_handler(THD *thd, uint log_type) } -bool Log_to_csv_event_handler::flush(THD *thd, TABLE_LIST *close_slow_log, - TABLE_LIST *close_general_log) +/* + Close log tables temporarily. The thread which closed + them this way can lock them in any mode it needs. + NOTE: one should call logger.lock() before entering this + function. +*/ +void Log_to_csv_event_handler::tmp_close_log_tables(THD *thd) { + TABLE_LIST close_slow_log, close_general_log; + + /* fill lists, we will need to perform operations on tables */ + bzero((char*) &close_slow_log, sizeof(TABLE_LIST)); + close_slow_log.alias= close_slow_log.table_name=(char*) "slow_log"; + close_slow_log.table_name_length= 8; + close_slow_log.db= (char*) "mysql"; + close_slow_log.db_length= 5; + + bzero((char*) &close_general_log, sizeof(TABLE_LIST)); + close_general_log.alias= close_general_log.table_name=(char*) "general_log"; + close_general_log.table_name_length= 11; + close_general_log.db= (char*) "mysql"; + close_general_log.db_length= 5; + + privileged_thread= thd; + VOID(pthread_mutex_lock(&LOCK_open)); + /* + NOTE: in fact, the first parameter used in query_cache_invalidate3() + could be any non-NULL THD, as the underlying code makes certain + assumptions about this. + Here we use one of the logger handler THD's. Simply because it + seems appropriate. + */ if (opt_log) { close_log_table(QUERY_LOG_GENERAL, TRUE); - query_cache_invalidate3(thd, close_general_log, 0); - unlock_table_name(thd, close_general_log); + query_cache_invalidate3(general_log_thd, &close_general_log, 0); } if (opt_slow_log) { close_log_table(QUERY_LOG_SLOW, TRUE); - query_cache_invalidate3(thd, close_slow_log, 0); - unlock_table_name(thd, close_slow_log); + query_cache_invalidate3(general_log_thd, &close_slow_log, 0); } VOID(pthread_mutex_unlock(&LOCK_open)); - /* - we use | and not || here, to ensure that both reopen_log_table - are called, even if the first one fails - */ - if ((opt_slow_log && reopen_log_table(QUERY_LOG_SLOW)) | - (opt_log && reopen_log_table(QUERY_LOG_GENERAL))) - return 1; - return 0; } /* the parameters are unused for the log tables */ @@ -1199,21 +1290,20 @@ void Log_to_csv_event_handler:: THD *log_thd, *curr= current_thd; TABLE_LIST *table; + if (!logger.is_log_table_enabled(log_table_type)) + return; /* do nothing */ + switch (log_table_type) { case QUERY_LOG_GENERAL: - if (!logger.is_general_log_table_enabled()) - return; /* do nothing */ log_thd= general_log_thd; table= &general_log; break; case QUERY_LOG_SLOW: - if (!logger.is_slow_log_table_enabled()) - return; /* do nothing */ log_thd= slow_log_thd; table= &slow_log; break; default: - DBUG_ASSERT(0); + assert(0); // Impossible } /* @@ -1268,7 +1358,7 @@ binlog_trans_log_savepos(THD *thd, my_off_t *pos) (binlog_trx_data*) thd->ha_data[binlog_hton->slot]; DBUG_ASSERT(mysql_bin_log.is_open()); *pos= trx_data->position(); - DBUG_PRINT("return", ("*pos=%u", *pos)); + DBUG_PRINT("return", ("*pos: %lu", (ulong) *pos)); DBUG_VOID_RETURN; } @@ -1292,7 +1382,7 @@ static void binlog_trans_log_truncate(THD *thd, my_off_t pos) { DBUG_ENTER("binlog_trans_log_truncate"); - DBUG_PRINT("enter", ("pos=%u", pos)); + DBUG_PRINT("enter", ("pos: %lu", (ulong) pos)); DBUG_ASSERT(thd->ha_data[binlog_hton->slot] != NULL); /* Only true if binlog_trans_log_savepos() wasn't called before */ @@ -1368,8 +1458,8 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data, DBUG_ENTER("binlog_end_trans"); int error=0; IO_CACHE *trans_log= &trx_data->trans_log; - DBUG_PRINT("enter", ("transaction: %s, end_ev=%p", - all ? "all" : "stmt", end_ev)); + DBUG_PRINT("enter", ("transaction: %s end_ev: 0x%lx", + all ? "all" : "stmt", (long) end_ev)); DBUG_PRINT("info", ("thd->options={ %s%s}", FLAGSTR(thd->options, OPTION_NOT_AUTOCOMMIT), FLAGSTR(thd->options, OPTION_BEGIN))); @@ -1392,9 +1482,7 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data, were, we would have to ensure that we're not ending a statement inside a stored function. */ -#ifdef HAVE_ROW_BASED_REPLICATION thd->binlog_flush_pending_rows_event(TRUE); -#endif /* We write the transaction cache to the binary log if either we're committing the entire transaction, or if we are doing an @@ -1404,13 +1492,11 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data, { error= mysql_bin_log.write(thd, &trx_data->trans_log, end_ev); trx_data->reset(); -#ifdef HAVE_ROW_BASED_REPLICATION /* We need to step the table map version after writing the transaction cache to disk. */ mysql_bin_log.update_table_map_version(); -#endif statistic_increment(binlog_cache_use, &LOCK_status); if (trans_log->disk_writes != 0) { @@ -1419,7 +1505,6 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data, } } } -#ifdef HAVE_ROW_BASED_REPLICATION else { /* @@ -1428,12 +1513,11 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data, If rolling back a statement in a transaction, we truncate the transaction cache to remove the statement. - */ if (all || !(thd->options & (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT))) trx_data->reset(); - else - trx_data->truncate(trx_data->before_stmt_pos); // ...statement + else // ...statement + trx_data->truncate(trx_data->before_stmt_pos); /* We need to step the table map version on a rollback to ensure @@ -1442,7 +1526,6 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data, */ mysql_bin_log.update_table_map_version(); } -#endif DBUG_RETURN(error); } @@ -1453,18 +1536,16 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all) do nothing. just pretend we can do 2pc, so that MySQL won't switch to 1pc. - real work will be done in TC_LOG_BINLOG::log() + real work will be done in MYSQL_BIN_LOG::log_xid() */ return 0; } static int binlog_commit(handlerton *hton, THD *thd, bool all) { - int error= 0; DBUG_ENTER("binlog_commit"); binlog_trx_data *const trx_data= (binlog_trx_data*) thd->ha_data[binlog_hton->slot]; - IO_CACHE *trans_log= &trx_data->trans_log; DBUG_ASSERT(mysql_bin_log.is_open()); /* @@ -1475,7 +1556,7 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all) if ((all || !(thd->options & (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT))) && trx_data->empty()) { - // we're here because trans_log was flushed in TC_LOG_BINLOG::log() + // we're here because trans_log was flushed in MYSQL_BIN_LOG::log_xid() trx_data->reset(); DBUG_RETURN(0); } @@ -1499,7 +1580,6 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all) int error=0; binlog_trx_data *const trx_data= (binlog_trx_data*) thd->ha_data[binlog_hton->slot]; - IO_CACHE *trans_log= &trx_data->trans_log; DBUG_ASSERT(mysql_bin_log.is_open()); if (trx_data->empty()) { @@ -1562,9 +1642,6 @@ static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv) static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv) { DBUG_ENTER("binlog_savepoint_rollback"); - binlog_trx_data *const trx_data= - (binlog_trx_data*) thd->ha_data[binlog_hton->slot]; - IO_CACHE *trans_log= &trx_data->trans_log; DBUG_ASSERT(mysql_bin_log.is_open()); /* @@ -1575,7 +1652,7 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv) if (unlikely(thd->options & (OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG))) { - int const error= + int error= thd->binlog_query(THD::STMT_QUERY_TYPE, thd->query, thd->query_length, TRUE, FALSE); DBUG_RETURN(error); @@ -1584,6 +1661,7 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv) DBUG_RETURN(0); } + int check_binlog_magic(IO_CACHE* log, const char** errmsg) { char magic[4]; @@ -1604,6 +1682,7 @@ int check_binlog_magic(IO_CACHE* log, const char** errmsg) return 0; } + File open_binlog(IO_CACHE *log, const char *log_file_name, const char **errmsg) { File file; @@ -1662,7 +1741,7 @@ void setup_windows_event_source() /* Register EventMessageFile */ dwError = RegSetValueEx(hRegKey, "EventMessageFile", 0, REG_EXPAND_SZ, - (PBYTE) szPath, strlen(szPath)+1); + (PBYTE) szPath, (DWORD) (strlen(szPath) + 1)); /* Register supported event types */ dwTypes= (EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | @@ -2027,8 +2106,8 @@ bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host, if (my_b_write(&log_file, (byte*) "\t\t" ,2) < 0) goto err; - /* command_type, thread_id */ - length= my_snprintf(buff, 32, "%5ld ", thread_id); + /* command_type, thread_id */ + length= my_snprintf(buff, 32, "%5ld ", (long) thread_id); if (my_b_write(&log_file, (byte*) buff, length)) goto err; @@ -2110,7 +2189,6 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT)) { - Security_context *sctx= thd->security_ctx; if (current_time != last_time) { last_time= current_time; @@ -2218,7 +2296,7 @@ const char *MYSQL_LOG::generate_name(const char *log_name, TODO: The following should be using fn_format(); We just need to first change fn_format() to cut the file name if it's too long. */ - strmake(buff, glob_hostname, FN_REFLEN - 5); + strmake(buff, pidfile_name, FN_REFLEN - 5); strmov(fn_ext(buff), suffix); return (const char *)buff; } @@ -2348,9 +2426,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name, ulong max_size_arg, bool null_created_arg) { - char buff[FN_REFLEN]; File file= -1; - int open_flags = O_CREAT | O_BINARY; DBUG_ENTER("MYSQL_BIN_LOG::open"); DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg)); @@ -2512,6 +2588,8 @@ int MYSQL_BIN_LOG::raw_get_current_log(LOG_INFO* linfo) 0 ok */ +#ifdef HAVE_REPLICATION + static bool copy_up_file_and_fill(IO_CACHE *index_file, my_off_t offset) { int bytes_read; @@ -2545,6 +2623,7 @@ err: DBUG_RETURN(1); } +#endif /* HAVE_REPLICATION */ /* Find the position in the log-index-file for the given log name @@ -2903,6 +2982,7 @@ int MYSQL_BIN_LOG::purge_logs(const char *to_log, ulonglong *decrease_log_space) { int error; + int ret = 0; bool exit_loop= 0; LOG_INFO log_info; DBUG_ENTER("purge_logs"); @@ -2947,6 +3027,14 @@ int MYSQL_BIN_LOG::purge_logs(const char *to_log, *decrease_log_space-= file_size; ha_binlog_index_purge_file(current_thd, log_info.log_file_name); + if (current_thd->query_error) { + DBUG_PRINT("info",("query error: %d", current_thd->query_error)); + if (my_errno == EMFILE) { + DBUG_PRINT("info",("my_errno: %d, set ret = LOG_INFO_EMFILE", my_errno)); + ret = LOG_INFO_EMFILE; + break; + } + } if (find_next_log(&log_info, 0) || exit_loop) break; @@ -2957,6 +3045,9 @@ int MYSQL_BIN_LOG::purge_logs(const char *to_log, the log index file after restart - otherwise, this should be safe */ error= update_log_index(&log_info, need_update_threads); + if (error == 0) { + error = ret; + } err: if (need_mutex) @@ -3025,8 +3116,6 @@ err: pthread_mutex_unlock(&LOCK_index); DBUG_RETURN(error); } - - #endif /* HAVE_REPLICATION */ @@ -3150,7 +3239,6 @@ void MYSQL_BIN_LOG::new_file_impl(bool need_lock) We log the whole file name for log file as the user may decide to change base names at some point. */ - THD *thd = current_thd; /* may be 0 if we are reacting to SIGHUP */ Rotate_log_event r(new_name+dirname_length(new_name), 0, LOG_EVENT_OFFSET, 0); r.write(&log_file); @@ -3265,13 +3353,13 @@ bool MYSQL_BIN_LOG::flush_and_sync() return err; } -void MYSQL_BIN_LOG::start_union_events(THD *thd) +void MYSQL_BIN_LOG::start_union_events(THD *thd, query_id_t query_id_param) { DBUG_ASSERT(!thd->binlog_evt_union.do_union); thd->binlog_evt_union.do_union= TRUE; thd->binlog_evt_union.unioned_events= FALSE; thd->binlog_evt_union.unioned_events_trans= FALSE; - thd->binlog_evt_union.first_query_id= thd->query_id; + thd->binlog_evt_union.first_query_id= query_id_param; } void MYSQL_BIN_LOG::stop_union_events(THD *thd) @@ -3317,7 +3405,6 @@ int THD::binlog_setup_trx_data() DBUG_RETURN(0); } -#ifdef HAVE_ROW_BASED_REPLICATION /* Function to start a statement and optionally a transaction for the binary log. @@ -3349,27 +3436,17 @@ int THD::binlog_setup_trx_data() void THD::binlog_start_trans_and_stmt() { - DBUG_ENTER("binlog_start_trans_and_stmt"); binlog_trx_data *trx_data= (binlog_trx_data*) ha_data[binlog_hton->slot]; - DBUG_PRINT("enter", ("trx_data=0x%lu", trx_data)); - if (trx_data) - DBUG_PRINT("enter", ("trx_data->before_stmt_pos=%u", - trx_data->before_stmt_pos)); + DBUG_ENTER("binlog_start_trans_and_stmt"); + DBUG_PRINT("enter", ("trx_data: 0x%lx trx_data->before_stmt_pos: %lu", + (long) trx_data, + (trx_data ? (ulong) trx_data->before_stmt_pos : + (ulong) 0))); + if (trx_data == NULL || trx_data->before_stmt_pos == MY_OFF_T_UNDEF) { - /* - The call to binlog_trans_log_savepos() might create the trx_data - structure, if it didn't exist before, so we save the position - into an auto variable and then write it into the transaction - data for the binary log (i.e., trx_data). - */ - my_off_t pos= 0; - binlog_trans_log_savepos(this, &pos); - trx_data= (binlog_trx_data*) ha_data[binlog_hton->slot]; - - trx_data->before_stmt_pos= pos; - + this->binlog_set_stmt_begin(); if (options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) trans_register_ha(this, TRUE, binlog_hton); trans_register_ha(this, FALSE, binlog_hton); @@ -3377,6 +3454,51 @@ THD::binlog_start_trans_and_stmt() DBUG_VOID_RETURN; } +void THD::binlog_set_stmt_begin() { + binlog_trx_data *trx_data= + (binlog_trx_data*) ha_data[binlog_hton->slot]; + + /* + The call to binlog_trans_log_savepos() might create the trx_data + structure, if it didn't exist before, so we save the position + into an auto variable and then write it into the transaction + data for the binary log (i.e., trx_data). + */ + my_off_t pos= 0; + binlog_trans_log_savepos(this, &pos); + trx_data= (binlog_trx_data*) ha_data[binlog_hton->slot]; + trx_data->before_stmt_pos= pos; +} + +int THD::binlog_flush_transaction_cache() +{ + DBUG_ENTER("binlog_flush_transaction_cache"); + binlog_trx_data *trx_data= (binlog_trx_data*) ha_data[binlog_hton->slot]; + DBUG_PRINT("enter", ("trx_data=0x%lu", (ulong) trx_data)); + if (trx_data) + DBUG_PRINT("enter", ("trx_data->before_stmt_pos=%lu", + (ulong) trx_data->before_stmt_pos)); + + /* + Write the transaction cache to the binary log. We don't flush and + sync the log file since we don't know if more will be written to + it. If the caller want the log file sync:ed, the caller has to do + it. + + The transaction data is only reset upon a successful write of the + cache to the binary log. + */ + + if (trx_data && likely(mysql_bin_log.is_open())) { + if (int error= mysql_bin_log.write_cache(&trx_data->trans_log, true, true)) + DBUG_RETURN(error); + trx_data->reset(); + } + + DBUG_RETURN(0); +} + + /* Write a table map to the binary log. */ @@ -3385,8 +3507,8 @@ int THD::binlog_write_table_map(TABLE *table, bool is_trans) { int error; DBUG_ENTER("THD::binlog_write_table_map"); - DBUG_PRINT("enter", ("table: %0xlx (%s: #%u)", - (long) table, table->s->table_name, + DBUG_PRINT("enter", ("table: 0x%lx (%s: #%lu)", + (long) table, table->s->table_name.str, table->s->table_map_id)); /* Pre-conditions */ @@ -3449,7 +3571,7 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd, { DBUG_ENTER("MYSQL_BIN_LOG::flush_and_set_pending_rows_event(event)"); DBUG_ASSERT(mysql_bin_log.is_open()); - DBUG_PRINT("enter", ("event=%p", event)); + DBUG_PRINT("enter", ("event: 0x%lx", (long) event)); int error= 0; @@ -3458,7 +3580,7 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd, DBUG_ASSERT(trx_data); - DBUG_PRINT("info", ("trx_data->pending()=%p", trx_data->pending())); + DBUG_PRINT("info", ("trx_data->pending(): 0x%lx", (long) trx_data->pending())); if (Rows_log_event* pending= trx_data->pending()) { @@ -3525,7 +3647,6 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd, DBUG_RETURN(error); } -#endif /*HAVE_ROW_BASED_REPLICATION*/ /* Write an event to the binary log @@ -3558,11 +3679,9 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) we are inside a stored function, we do not end the statement since this will close all tables on the slave. */ -#ifdef HAVE_ROW_BASED_REPLICATION bool const end_stmt= thd->prelocked_mode && thd->lex->requires_prelocking(); thd->binlog_flush_pending_rows_event(end_stmt); -#endif /*HAVE_ROW_BASED_REPLICATION*/ pthread_mutex_lock(&LOCK_log); @@ -3573,14 +3692,14 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) */ if (likely(is_open())) { - const char *local_db= event_info->get_db(); IO_CACHE *file= &log_file; #ifdef HAVE_REPLICATION /* - In the future we need to add to the following if tests like - "do the involved tables match (to be implemented) - binlog_[wild_]{do|ignore}_table?" (WL#1049)" + In the future we need to add to the following if tests like + "do the involved tables match (to be implemented) + binlog_[wild_]{do|ignore}_table?" (WL#1049)" */ + const char *local_db= event_info->get_db(); if ((thd && !(thd->options & OPTION_BIN_LOG)) || (!binlog_filter->db_ok(local_db))) { @@ -3592,7 +3711,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) } #endif /* HAVE_REPLICATION */ -#if defined(USING_TRANSACTIONS) && defined(HAVE_ROW_BASED_REPLICATION) +#if defined(USING_TRANSACTIONS) /* Should we write to the binlog cache or to the binlog on disk? Write to the binlog cache if: @@ -3613,9 +3732,9 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) my_off_t trans_log_pos= my_b_tell(trans_log); if (event_info->get_cache_stmt() || trans_log_pos != 0) { - DBUG_PRINT("info", ("Using trans_log: cache=%d, trans_log_pos=%u", + DBUG_PRINT("info", ("Using trans_log: cache: %d, trans_log_pos: %lu", event_info->get_cache_stmt(), - trans_log_pos)); + (ulong) trans_log_pos)); if (trans_log_pos == 0) thd->binlog_start_trans_and_stmt(); file= trans_log; @@ -3627,7 +3746,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) LOCK_log. */ } -#endif /* USING_TRANSACTIONS && HAVE_ROW_BASED_REPLICATION */ +#endif /* USING_TRANSACTIONS */ DBUG_PRINT("info",("event type: %d",event_info->get_type_code())); /* @@ -3657,15 +3776,17 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) } if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0) { - DBUG_PRINT("info",("number of auto_inc intervals: %lu", - thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements())); + DBUG_PRINT("info",("number of auto_inc intervals: %u", + thd->auto_inc_intervals_in_cur_stmt_for_binlog. + nb_elements())); /* If the auto_increment was second in a table's index (possible with MyISAM or BDB) (table->next_number_key_offset != 0), such event is in fact not necessary. We could avoid logging it. */ - Intvar_log_event e(thd,(uchar) INSERT_ID_EVENT, - thd->auto_inc_intervals_in_cur_stmt_for_binlog.minimum()); + Intvar_log_event e(thd, (uchar) INSERT_ID_EVENT, + thd->auto_inc_intervals_in_cur_stmt_for_binlog. + minimum()); if (e.write(file)) goto err; } @@ -3767,7 +3888,7 @@ void MYSQL_BIN_LOG::rotate_and_purge(uint flags) #ifdef HAVE_REPLICATION if (expire_logs_days) { - long purge_time= time(0) - expire_logs_days*24*60*60; + long purge_time= (long) (time(0) - expire_logs_days*24*60*60); if (purge_time >= 0) purge_logs_before_date(purge_time); } @@ -3788,12 +3909,48 @@ uint MYSQL_BIN_LOG::next_file_id() /* + Write the contents of a cache to the binary log. + + SYNOPSIS + write_cache() + cache Cache to write to the binary log + lock_log True if the LOCK_log mutex should be aquired, false otherwise + sync_log True if the log should be flushed and sync:ed + + DESCRIPTION + Write the contents of the cache to the binary log. The cache will + be reset as a READ_CACHE to be able to read the contents from it. + */ + +int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log) +{ + Mutex_sentry sentry(lock_log ? &LOCK_log : NULL); + + if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0)) + return ER_ERROR_ON_WRITE; + uint bytes= my_b_bytes_in_cache(cache); + do + { + if (my_b_write(&log_file, cache->read_pos, bytes)) + return ER_ERROR_ON_WRITE; + cache->read_pos= cache->read_end; + } while ((bytes= my_b_fill(cache))); + + if (sync_log) + flush_and_sync(); + + return 0; // All OK +} + +/* Write a cached log entry to the binary log SYNOPSIS write() thd cache The cache to copy to the binlog + commit_event The commit event to print after writing the + contents of the cache. NOTE - We only come here if there is something in the cache. @@ -3818,8 +3975,6 @@ bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event) if (likely(is_open())) // Should always be true { - uint length; - /* We only bother to write to the binary log if there is anything to write. @@ -3853,25 +4008,12 @@ bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event) if (qinfo.write(&log_file)) goto err; } - /* Read from the file used to cache the queries .*/ - if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0)) - goto err; - length=my_b_bytes_in_cache(cache); - DBUG_EXECUTE_IF("half_binlogged_transaction", length-=100;); - do - { - /* Write data to the binary log file */ - if (my_b_write(&log_file, cache->read_pos, length)) - goto err; - cache->read_pos=cache->read_end; // Mark buffer used up - DBUG_EXECUTE_IF("half_binlogged_transaction", goto DBUG_skip_commit;); - } while ((length=my_b_fill(cache))); + if ((write_error= write_cache(cache, false, false))) + goto err; + if (commit_event && commit_event->write(&log_file)) goto err; -#ifndef DBUG_OFF - DBUG_skip_commit: -#endif if (flush_and_sync()) goto err; DBUG_EXECUTE_IF("half_binlogged_transaction", abort();); @@ -4384,7 +4526,7 @@ int TC_LOG_MMAP::open(const char *opt_name) goto err; if (using_heuristic_recover()) return 1; - if ((fd= my_create(logname, O_RDWR, 0, MYF(MY_WME))) < 0) + if ((fd= my_create(logname, CREATE_MODE, O_RDWR, MYF(MY_WME))) < 0) goto err; inited=1; file_length= opt_tc_log_size; @@ -4531,21 +4673,34 @@ int TC_LOG_MMAP::overflow() } /* - all access to active page is serialized but it's not a problem, as - we're assuming that fsync() will be a main bottleneck. - That is, parallelizing writes to log pages we'll decrease number of - threads waiting for a page, but then all these threads will be waiting - for a fsync() anyway + Record that transaction XID is committed on the persistent storage + + NOTES + This function is called in the middle of two-phase commit: + First all resources prepare the transaction, then tc_log->log() is called, + then all resources commit the transaction, then tc_log->unlog() is called. + + All access to active page is serialized but it's not a problem, as + we're assuming that fsync() will be a main bottleneck. + That is, parallelizing writes to log pages we'll decrease number of + threads waiting for a page, but then all these threads will be waiting + for a fsync() anyway + + IMPLEMENTATION + If tc_log == MYSQL_LOG then tc_log writes transaction to binlog and + records XID in a special Xid_log_event. + If tc_log = TC_LOG_MMAP then xid is written in a special memory-mapped + log. RETURN - 0 - error - otherwise - "cookie", a number that will be passed as an argument - to unlog() call. tc_log can define it any way it wants, - and use for whatever purposes. TC_LOG_MMAP sets it - to the position in memory where xid was logged to. + 0 Error + # "cookie", a number that will be passed as an argument + to unlog() call. tc_log can define it any way it wants, + and use for whatever purposes. TC_LOG_MMAP sets it + to the position in memory where xid was logged to. */ -int TC_LOG_MMAP::log(THD *thd, my_xid xid) +int TC_LOG_MMAP::log_xid(THD *thd, my_xid xid) { int err; PAGE *p; @@ -4654,6 +4809,7 @@ int TC_LOG_MMAP::sync() erase xid from the page, update page free space counters/pointers. cookie points directly to the memory where xid was logged */ + void TC_LOG_MMAP::unlog(ulong cookie, my_xid xid) { PAGE *p=pages+(cookie/tc_log_page_size); @@ -4896,7 +5052,7 @@ void TC_LOG_BINLOG::close() 0 - error 1 - success */ -int TC_LOG_BINLOG::log(THD *thd, my_xid xid) +int TC_LOG_BINLOG::log_xid(THD *thd, my_xid xid) { DBUG_ENTER("TC_LOG_BINLOG::log"); Xid_log_event xle(thd, xid); diff --git a/sql/log.h b/sql/log.h index 8f75601f02b..970823dcd4a 100644 --- a/sql/log.h +++ b/sql/log.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -34,7 +33,7 @@ class TC_LOG virtual int open(const char *opt_name)=0; virtual void close()=0; - virtual int log(THD *thd, my_xid xid)=0; + virtual int log_xid(THD *thd, my_xid xid)=0; virtual void unlog(ulong cookie, my_xid xid)=0; }; @@ -44,7 +43,7 @@ public: TC_LOG_DUMMY() {} int open(const char *opt_name) { return 0; } void close() { } - int log(THD *thd, my_xid xid) { return 1; } + int log_xid(THD *thd, my_xid xid) { return 1; } void unlog(ulong cookie, my_xid xid) { } }; @@ -89,7 +88,7 @@ class TC_LOG_MMAP: public TC_LOG TC_LOG_MMAP(): inited(0) {} int open(const char *opt_name); void close(); - int log(THD *thd, my_xid xid); + int log_xid(THD *thd, my_xid xid); void unlog(ulong cookie, my_xid xid); int recover(); @@ -114,6 +113,8 @@ extern TC_LOG_DUMMY tc_log_dummy; #define LOG_INFO_MEM -6 #define LOG_INFO_FATAL -7 #define LOG_INFO_IN_USE -8 +#define LOG_INFO_EMFILE -9 + /* bitmap to SQL_LOG::close() */ #define LOG_CLOSE_INDEX 1 @@ -286,7 +287,7 @@ public: int open(const char *opt_name); void close(); - int log(THD *thd, my_xid xid); + int log_xid(THD *thd, my_xid xid); void unlog(ulong cookie, my_xid xid); int recover(IO_CACHE *log, Format_description_log_event *fdle); #if !defined(MYSQL_CLIENT) @@ -338,7 +339,9 @@ public: bool write(Log_event* event_info); // binary log write bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event); - void start_union_events(THD *thd); + int write_cache(IO_CACHE *cache, bool lock_log, bool flush_and_sync); + + void start_union_events(THD *thd, query_id_t query_id_param); void stop_union_events(THD *thd); bool is_query_in_union(THD *thd, query_id_t query_id_param); @@ -404,6 +407,9 @@ public: }; +int check_if_log_table(uint db_len, const char *db, uint table_name_len, + const char *table_name, uint check_if_opened); + class Log_to_csv_event_handler: public Log_event_handler { /* @@ -412,6 +418,16 @@ class Log_to_csv_event_handler: public Log_event_handler THD's of the query. The reason is the locking order and duration. */ THD *general_log_thd, *slow_log_thd; + /* + This is for the thread, which called tmp_close_log_tables. The thread + will be allowed to write-lock the log tables (as it explicitly disabled + logging). This is used for such operations as REPAIR, which require + exclusive lock on the log tables. + NOTE: there can be only one priviliged thread, as one should + lock logger with logger.lock() before calling tmp_close_log_tables(). + So no other thread could get privileged status at the same time. + */ + THD *privileged_thread; friend class LOGGER; TABLE_LIST general_log, slow_log; @@ -436,13 +452,20 @@ public: const char *command_type, uint command_type_len, const char *sql_text, uint sql_text_len, CHARSET_INFO *client_cs); - bool flush(THD *thd, TABLE_LIST *close_slow_Log, - TABLE_LIST* close_general_log); + void tmp_close_log_tables(THD *thd); void close_log_table(uint log_type, bool lock_in_use); bool reopen_log_table(uint log_type); + THD* get_privileged_thread() + { + return privileged_thread; + } }; +/* type of the log table */ +#define QUERY_LOG_SLOW 1 +#define QUERY_LOG_GENERAL 2 + class Log_to_file_event_handler: public Log_event_handler { MYSQL_QUERY_LOG mysql_log; @@ -498,13 +521,18 @@ public: {} void lock() { (void) pthread_mutex_lock(&LOCK_logger); } void unlock() { (void) pthread_mutex_unlock(&LOCK_logger); } - bool is_general_log_table_enabled() + void tmp_close_log_tables(THD *thd); + bool is_log_table_enabled(uint log_table_type) { - return table_log_handler && table_log_handler->general_log.table != 0; - } - bool is_slow_log_table_enabled() - { - return table_log_handler && table_log_handler->slow_log.table != 0; + switch (log_table_type) { + case QUERY_LOG_SLOW: + return table_log_handler && table_log_handler->slow_log.table != 0; + case QUERY_LOG_GENERAL: + return table_log_handler && table_log_handler->general_log.table != 0; + default: + DBUG_ASSERT(0); + return FALSE; /* make compiler happy */ + } } /* We want to initialize all log mutexes as soon as possible, @@ -542,6 +570,7 @@ public: void close_log_table(uint log_type, bool lock_in_use); bool reopen_log_table(uint log_type); + bool reopen_log_tables(); /* we use this function to setup all enabled log event handlers */ int set_handlers(uint error_log_printer, @@ -564,18 +593,23 @@ public: return file_log_handler->get_mysql_log(); return NULL; } + THD* get_privileged_thread() + { + if (table_log_handler) + return table_log_handler->get_privileged_thread(); + else + return NULL; + } }; enum enum_binlog_format { BINLOG_FORMAT_STMT= 0, // statement-based -#ifdef HAVE_ROW_BASED_REPLICATION BINLOG_FORMAT_ROW= 1, // row_based /* statement-based except for cases where only row-based can work (UUID() etc): */ BINLOG_FORMAT_MIXED= 2, -#endif /* This value is last, after the end of binlog_format_typelib: it has no corresponding cell in this typelib. We use this value to be able to know if diff --git a/sql/log_event.cc b/sql/log_event.cc index 8fb1457088e..8e6311ce53a 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -141,7 +140,6 @@ static void pretty_print_str(IO_CACHE* cache, char* str, int len) } #endif /* MYSQL_CLIENT */ - #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) static void clear_all_errors(THD *thd, struct st_relay_log_info *rli) @@ -359,7 +357,7 @@ append_query_string(CHARSET_INFO *csinfo, else { *ptr++= '\''; - ptr+= escape_string_for_mysql(from->charset(), ptr, 0, + ptr+= escape_string_for_mysql(csinfo, ptr, 0, from->ptr(), from->length()); *ptr++='\''; } @@ -368,12 +366,14 @@ append_query_string(CHARSET_INFO *csinfo, } #endif + /* Prints a "session_var=value" string. Used by mysqlbinlog to print some SET commands just before it prints a query. */ #ifdef MYSQL_CLIENT + static void print_set_option(IO_CACHE* file, uint32 bits_changed, uint32 option, uint32 flags, const char* name, bool* need_comma) @@ -845,7 +845,7 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, LOG_EVENT_MINIMAL_HEADER_LEN); LOCK_MUTEX; - DBUG_PRINT("info", ("my_b_tell=%lu", my_b_tell(file))); + DBUG_PRINT("info", ("my_b_tell: %lu", (ulong) my_b_tell(file))); if (my_b_read(file, (byte *) head, header_size)) { DBUG_PRINT("info", ("Log_event::read_log_event(IO_CACHE*,Format_desc*) \ @@ -993,7 +993,7 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len, case FORMAT_DESCRIPTION_EVENT: ev = new Format_description_log_event(buf, event_len, description_event); break; -#if defined(HAVE_REPLICATION) && defined(HAVE_ROW_BASED_REPLICATION) +#if defined(HAVE_REPLICATION) case WRITE_ROWS_EVENT: ev = new Write_rows_log_event(buf, event_len, description_event); break; @@ -1144,13 +1144,18 @@ void Log_event::print_header(IO_CACHE* file, char emit_buf[256]; int const bytes_written= my_snprintf(emit_buf, sizeof(emit_buf), - "# %8.8lx %-48.48s |%s|\n# ", + "# %8.8lx %-48.48s |%s|\n", (unsigned long) (hexdump_from + (i & 0xfffffff0)), hex_string, char_string); DBUG_ASSERT(bytes_written >= 0); DBUG_ASSERT(static_cast<my_size_t>(bytes_written) < sizeof(emit_buf)); my_b_write(file, (byte*) emit_buf, bytes_written); } + /* + need a # to prefix the rest of printouts for example those of + Rows_log_event::print_helper(). + */ + my_b_write(file, reinterpret_cast<const byte*>("# "), 2); } DBUG_VOID_RETURN; } @@ -1161,11 +1166,10 @@ void Log_event::print_base64(IO_CACHE* file, bool more) { const uchar *ptr= (const uchar *)temp_buf; - my_off_t size= uint4korr(ptr + EVENT_LEN_OFFSET); - + uint32 size= uint4korr(ptr + EVENT_LEN_OFFSET); DBUG_ENTER("Log_event::print_base64"); - size_t const tmp_str_sz= base64_needed_encoded_length(size); + size_t const tmp_str_sz= base64_needed_encoded_length((int) size); char *const tmp_str= (char *) my_malloc(tmp_str_sz, MYF(MY_WME)); if (!tmp_str) { fprintf(stderr, "\nError: Out of memory. " @@ -1173,8 +1177,10 @@ void Log_event::print_base64(IO_CACHE* file, DBUG_VOID_RETURN; } - int const res= base64_encode(ptr, size, tmp_str); - DBUG_ASSERT(res == 0); + if (base64_encode(ptr, (size_t) size, tmp_str)) + { + DBUG_ASSERT(0); + } if (my_b_tell(file) == 0) my_b_printf(file, "\nBINLOG '\n"); @@ -1182,7 +1188,7 @@ void Log_event::print_base64(IO_CACHE* file, my_b_printf(file, "%s\n", tmp_str); if (!more) - my_b_printf(file, "';\n"); + my_b_printf(file, "'%s\n", print_event_info->delimiter); my_free(tmp_str, MYF(0)); DBUG_VOID_RETURN; @@ -1288,7 +1294,9 @@ bool Query_log_event::write(IO_CACHE* file) 1+1+FN_REFLEN+ // code of catalog and catalog length and catalog 1+4+ // code of autoinc and the 2 autoinc variables 1+6+ // code of charset and charset - 1+1+MAX_TIME_ZONE_NAME_LENGTH // code of tz and tz length and tz name + 1+1+MAX_TIME_ZONE_NAME_LENGTH+ // code of tz and tz length and tz name + 1+2+ // code of lc_time_names and lc_time_names_number + 1+2 // code of charset_database and charset_database_number ], *start, *start_of_status; ulong event_length; @@ -1400,6 +1408,20 @@ bool Query_log_event::write(IO_CACHE* file) memcpy(start, time_zone_str, time_zone_len); start+= time_zone_len; } + if (lc_time_names_number) + { + DBUG_ASSERT(lc_time_names_number <= 0xFFFF); + *start++= Q_LC_TIME_NAMES_CODE; + int2store(start, lc_time_names_number); + start+= 2; + } + if (charset_database_number) + { + DBUG_ASSERT(charset_database_number <= 0xFFFF); + *start++= Q_CHARSET_DATABASE_CODE; + int2store(start, charset_database_number); + start+= 2; + } /* Here there could be code like if (command-line-option-which-says-"log_this_variable" && inited) @@ -1412,6 +1434,7 @@ bool Query_log_event::write(IO_CACHE* file) /* Store length of status variables */ status_vars_len= (uint) (start-start_of_status); + DBUG_ASSERT(status_vars_len <= MAX_SIZE_LOG_EVENT_STATUS); int2store(buf + Q_STATUS_VARS_LEN_OFFSET, status_vars_len); /* @@ -1463,7 +1486,9 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, flags2_inited(1), sql_mode_inited(1), charset_inited(1), sql_mode(thd_arg->variables.sql_mode), auto_increment_increment(thd_arg->variables.auto_increment_increment), - auto_increment_offset(thd_arg->variables.auto_increment_offset) + auto_increment_offset(thd_arg->variables.auto_increment_offset), + lc_time_names_number(thd_arg->variables.lc_time_names->number), + charset_database_number(0) { time_t end_time; time(&end_time); @@ -1471,6 +1496,9 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, catalog_len = (catalog) ? (uint32) strlen(catalog) : 0; /* status_vars_len is set just before writing the event */ db_len = (db) ? (uint32) strlen(db) : 0; + if (thd_arg->variables.collation_database != thd_arg->db_charset) + charset_database_number= thd_arg->variables.collation_database->number; + /* If we don't use flags2 for anything else than options contained in thd->options, it would be more efficient to flags2=thd_arg->options @@ -1497,30 +1525,38 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, } else time_zone_len= 0; - DBUG_PRINT("info",("Query_log_event has flags2=%lu sql_mode=%lu",flags2,sql_mode)); + DBUG_PRINT("info",("Query_log_event has flags2: %lu sql_mode: %lu", + (ulong) flags2, sql_mode)); } #endif /* MYSQL_CLIENT */ /* 2 utility functions for the next method */ -static void get_str_len_and_pointer(const char **dst, const char **src, uint *len) +/* + Get the pointer for a string (src) that contains the length in + the first byte. Set the output string (dst) to the string value + and place the length of the string in the byte after the string. +*/ +static void get_str_len_and_pointer(const Log_event::Byte **src, + const char **dst, + uint *len) { if ((*len= **src)) - *dst= *src + 1; // Will be copied later - (*src)+= *len+1; + *dst= (char *)*src + 1; // Will be copied later + (*src)+= *len + 1; } - -static void copy_str_and_move(char **dst, const char **src, uint len) +static void copy_str_and_move(const char **src, + Log_event::Byte **dst, + uint len) { memcpy(*dst, *src, len); - *src= *dst; + *src= (const char *)*dst; (*dst)+= len; *(*dst)++= 0; } - /* Query_log_event::Query_log_event() This is used by the SQL slave thread to prepare the event before execution. @@ -1533,19 +1569,19 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, db(NullS), catalog_len(0), status_vars_len(0), flags2_inited(0), sql_mode_inited(0), charset_inited(0), auto_increment_increment(1), auto_increment_offset(1), - time_zone_len(0) + time_zone_len(0), lc_time_names_number(0), charset_database_number(0) { ulong data_len; uint32 tmp; uint8 common_header_len, post_header_len; - char *start; - const char *end; + Log_event::Byte *start; + const Log_event::Byte *end; bool catalog_nz= 1; DBUG_ENTER("Query_log_event::Query_log_event(char*,...)"); common_header_len= description_event->common_header_len; post_header_len= description_event->post_header_len[event_type-1]; - DBUG_PRINT("info",("event_len=%ld, common_header_len=%d, post_header_len=%d", + DBUG_PRINT("info",("event_len: %u common_header_len: %d post_header_len: %d", event_len, common_header_len, post_header_len)); /* @@ -1585,15 +1621,15 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, /* variable-part: the status vars; only in MySQL 5.0 */ - start= (char*) (buf+post_header_len); - end= (const char*) (start+status_vars_len); - for (const uchar* pos= (const uchar*) start; pos < (const uchar*) end;) + start= (Log_event::Byte*) (buf+post_header_len); + end= (const Log_event::Byte*) (start+status_vars_len); + for (const Log_event::Byte* pos= start; pos < end;) { switch (*pos++) { case Q_FLAGS2_CODE: flags2_inited= 1; flags2= uint4korr(pos); - DBUG_PRINT("info",("In Query_log_event, read flags2: %lu", flags2)); + DBUG_PRINT("info",("In Query_log_event, read flags2: %lu", (ulong) flags2)); pos+= 4; break; case Q_SQL_MODE_CODE: @@ -1609,7 +1645,7 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, break; } case Q_CATALOG_NZ_CODE: - get_str_len_and_pointer(&catalog, (const char **)(&pos), &catalog_len); + get_str_len_and_pointer(&pos, &catalog, &catalog_len); break; case Q_AUTO_INCREMENT: auto_increment_increment= uint2korr(pos); @@ -1625,7 +1661,7 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, } case Q_TIME_ZONE_CODE: { - get_str_len_and_pointer(&time_zone_str, (const char **)(&pos), &time_zone_len); + get_str_len_and_pointer(&pos, &time_zone_str, &time_zone_len); break; } case Q_CATALOG_CODE: /* for 5.0.x where 0<=x<=3 masters */ @@ -1634,6 +1670,14 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, pos+= catalog_len+2; // leap over end 0 catalog_nz= 0; // catalog has end 0 in event break; + case Q_LC_TIME_NAMES_CODE: + lc_time_names_number= uint2korr(pos); + pos+= 2; + break; + case Q_CHARSET_DATABASE_CODE: + charset_database_number= uint2korr(pos); + pos+= 2; + break; default: /* That's why you must write status vars in growing order of code */ DBUG_PRINT("info",("Query_log_event has unknown status vars (first has\ @@ -1643,38 +1687,38 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, } #if !defined(MYSQL_CLIENT) && defined(HAVE_QUERY_CACHE) - if (!(start= data_buf = (char*) my_malloc(catalog_len + 1 + - time_zone_len + 1 + - data_len + 1 + - QUERY_CACHE_FLAGS_SIZE + - db_len + 1, - MYF(MY_WME)))) + if (!(start= data_buf = (Log_event::Byte*) my_malloc(catalog_len + 1 + + time_zone_len + 1 + + data_len + 1 + + QUERY_CACHE_FLAGS_SIZE + + db_len + 1, + MYF(MY_WME)))) #else - if (!(start= data_buf = (char*) my_malloc(catalog_len + 1 + - time_zone_len + 1 + - data_len + 1, - MYF(MY_WME)))) + if (!(start= data_buf = (Log_event::Byte*) my_malloc(catalog_len + 1 + + time_zone_len + 1 + + data_len + 1, + MYF(MY_WME)))) #endif DBUG_VOID_RETURN; if (catalog_len) // If catalog is given { if (likely(catalog_nz)) // true except if event comes from 5.0.0|1|2|3. - copy_str_and_move(&start, &catalog, catalog_len); + copy_str_and_move(&catalog, &start, catalog_len); else { memcpy(start, catalog, catalog_len+1); // copy end 0 - catalog= start; + catalog= (const char *)start; start+= catalog_len+1; } } if (time_zone_len) - copy_str_and_move(&start, &time_zone_str, time_zone_len); + copy_str_and_move(&time_zone_str, &start, time_zone_len); /* A 2nd variable part; this is common to all versions */ memcpy((char*) start, end, data_len); // Copy db and query start[data_len]= '\0'; // End query with \0 (For safetly) - db= start; - query= start + db_len + 1; + db= (char *)start; + query= (char *)(start + db_len + 1); q_len= data_len - db_len -1; DBUG_VOID_RETURN; } @@ -1706,15 +1750,16 @@ void Query_log_event::print_query_header(IO_CACHE* file, if (different_db= memcmp(print_event_info->db, db, db_len + 1)) memcpy(print_event_info->db, db, db_len + 1); if (db[0] && different_db) - my_b_printf(file, "use %s;\n", db); + my_b_printf(file, "use %s%s\n", db, print_event_info->delimiter); } end=int10_to_str((long) when, strmov(buff,"SET TIMESTAMP="),10); - *end++=';'; + end= strmov(end, print_event_info->delimiter); *end++='\n'; my_b_write(file, (byte*) buff, (uint) (end-buff)); if (flags & LOG_EVENT_THREAD_SPECIFIC_F) - my_b_printf(file,"SET @@session.pseudo_thread_id=%lu;\n",(ulong)thread_id); + my_b_printf(file,"SET @@session.pseudo_thread_id=%lu%s\n", + (ulong)thread_id, print_event_info->delimiter); /* If flags2_inited==0, this is an event from 3.23 or 4.0; nothing to @@ -1743,7 +1788,7 @@ void Query_log_event::print_query_header(IO_CACHE* file, "@@session.sql_auto_is_null", &need_comma); print_set_option(file, tmp, OPTION_RELAXED_UNIQUE_CHECKS, ~flags2, "@@session.unique_checks", &need_comma); - my_b_printf(file,";\n"); + my_b_printf(file,"%s\n", print_event_info->delimiter); print_event_info->flags2= flags2; } } @@ -1771,15 +1816,17 @@ void Query_log_event::print_query_header(IO_CACHE* file, } if (unlikely(print_event_info->sql_mode != sql_mode)) { - my_b_printf(file,"SET @@session.sql_mode=%lu;\n",(ulong)sql_mode); + my_b_printf(file,"SET @@session.sql_mode=%lu%s\n", + (ulong)sql_mode, print_event_info->delimiter); print_event_info->sql_mode= sql_mode; } } if (print_event_info->auto_increment_increment != auto_increment_increment || print_event_info->auto_increment_offset != auto_increment_offset) { - my_b_printf(file,"SET @@session.auto_increment_increment=%lu, @@session.auto_increment_offset=%lu;\n", - auto_increment_increment,auto_increment_offset); + my_b_printf(file,"SET @@session.auto_increment_increment=%lu, @@session.auto_increment_offset=%lu%s\n", + auto_increment_increment,auto_increment_offset, + print_event_info->delimiter); print_event_info->auto_increment_increment= auto_increment_increment; print_event_info->auto_increment_offset= auto_increment_offset; } @@ -1799,16 +1846,18 @@ void Query_log_event::print_query_header(IO_CACHE* file, if (cs_info) { /* for mysql client */ - my_b_printf(file, "/*!\\C %s */;\n", cs_info->csname); + my_b_printf(file, "/*!\\C %s */%s\n", + cs_info->csname, print_event_info->delimiter); } my_b_printf(file,"SET " "@@session.character_set_client=%d," "@@session.collation_connection=%d," "@@session.collation_server=%d" - ";\n", + "%s\n", uint2korr(charset), uint2korr(charset+2), - uint2korr(charset+4)); + uint2korr(charset+4), + print_event_info->delimiter); memcpy(print_event_info->charset, charset, 6); } } @@ -1816,10 +1865,27 @@ void Query_log_event::print_query_header(IO_CACHE* file, { if (bcmp(print_event_info->time_zone_str, time_zone_str, time_zone_len+1)) { - my_b_printf(file,"SET @@session.time_zone='%s';\n", time_zone_str); + my_b_printf(file,"SET @@session.time_zone='%s'%s\n", + time_zone_str, print_event_info->delimiter); memcpy(print_event_info->time_zone_str, time_zone_str, time_zone_len+1); } } + if (lc_time_names_number != print_event_info->lc_time_names_number) + { + my_b_printf(file, "SET @@session.lc_time_names=%d%s\n", + lc_time_names_number, print_event_info->delimiter); + print_event_info->lc_time_names_number= lc_time_names_number; + } + if (charset_database_number != print_event_info->charset_database_number) + { + if (charset_database_number) + my_b_printf(file, "SET @@session.collation_database=%d%s\n", + charset_database_number, print_event_info->delimiter); + else + my_b_printf(file, "SET @@session.collation_database=DEFAULT%s\n", + print_event_info->delimiter); + print_event_info->charset_database_number= charset_database_number; + } } @@ -1829,7 +1895,7 @@ void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) print_query_header(&cache, print_event_info); my_b_write(&cache, (byte*) query, q_len); - my_b_printf(&cache, ";\n"); + my_b_printf(&cache, "%s\n", print_event_info->delimiter); } #endif /* MYSQL_CLIENT */ @@ -1963,7 +2029,34 @@ int Query_log_event::do_apply_event(RELAY_LOG_INFO const *rli, goto compare_errors; } } - + if (lc_time_names_number) + { + if (!(thd->variables.lc_time_names= + my_locale_by_number(lc_time_names_number))) + { + my_printf_error(ER_UNKNOWN_ERROR, + "Unknown locale: '%d'", MYF(0), lc_time_names_number); + thd->variables.lc_time_names= &my_locale_en_US; + goto compare_errors; + } + } + else + thd->variables.lc_time_names= &my_locale_en_US; + if (charset_database_number) + { + CHARSET_INFO *cs; + if (!(cs= get_charset(charset_database_number, MYF(0)))) + { + char buf[20]; + int10_to_str((int) charset_database_number, buf, -10); + my_error(ER_UNKNOWN_COLLATION, MYF(0), buf); + goto compare_errors; + } + thd->variables.collation_database= cs; + } + else + thd->variables.collation_database= thd->db_charset; + /* Execute the query (note that we bypass dispatch_command()) */ mysql_parse(thd, thd->query, thd->query_length); @@ -2197,9 +2290,9 @@ void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info) and rollback unfinished transaction. Probably this can be done with RESET CONNECTION (syntax to be defined). */ - my_b_printf(&cache,"RESET CONNECTION;\n"); + my_b_printf(&cache,"RESET CONNECTION%s\n", print_event_info->delimiter); #else - my_b_printf(&cache,"ROLLBACK;\n"); + my_b_printf(&cache,"ROLLBACK%s\n", print_event_info->delimiter); #endif } DBUG_VOID_RETURN; @@ -2218,6 +2311,8 @@ Start_log_event_v3::Start_log_event_v3(const char* buf, binlog_version= uint2korr(buf+ST_BINLOG_VER_OFFSET); memcpy(server_version, buf+ST_SERVER_VER_OFFSET, ST_SERVER_VER_LEN); + // prevent overrun if log is corrupted on disk + server_version[ST_SERVER_VER_LEN-1]= 0; created= uint4korr(buf+ST_CREATED_OFFSET); /* We use log_pos to mark if this was an artificial event or not */ artificial_event= (log_pos == 0); @@ -2341,6 +2436,8 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver) switch (binlog_ver) { case 4: /* MySQL 5.0 */ memcpy(server_version, ::server_version, ST_SERVER_VER_LEN); + DBUG_EXECUTE_IF("pretend_version_50034_in_binlog", + strmov(server_version, "5.0.34");); common_header_len= LOG_EVENT_HEADER_LEN; number_of_event_types= LOG_EVENT_TYPES; /* we'll catch my_malloc() error in is_valid() */ @@ -2431,6 +2528,7 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver) post_header_len= 0; /* will make is_valid() fail */ break; } + calc_server_version_split(); } @@ -2470,6 +2568,7 @@ Format_description_log_event(const char* buf, post_header_len= (uint8*) my_memdup((byte*)buf+ST_COMMON_HEADER_LEN_OFFSET+1, number_of_event_types* sizeof(*post_header_len), MYF(0)); + calc_server_version_split(); DBUG_VOID_RETURN; } @@ -2579,6 +2678,37 @@ Format_description_log_event::do_shall_skip(RELAY_LOG_INFO *rli) #endif + +/** + Splits the event's 'server_version' string into three numeric pieces stored + into 'server_version_split': + X.Y.Zabc (X,Y,Z numbers, a not a digit) -> {X,Y,Z} + X.Yabc -> {X,Y,0} + Xabc -> {X,0,0} + 'server_version_split' is then used for lookups to find if the server which + created this event has some known bug. +*/ +void Format_description_log_event::calc_server_version_split() +{ + char *p= server_version, *r; + ulong number; + for (uint i= 0; i<=2; i++) + { + number= strtoul(p, &r, 10); + server_version_split[i]= (uchar)number; + DBUG_ASSERT(number < 256); // fit in uchar + p= r; + DBUG_ASSERT(!((i == 0) && (*r != '.'))); // should be true in practice + if (*r == '.') + p++; // skip the dot + } + DBUG_PRINT("info",("Format_description_log_event::server_version_split:" + " '%s' %d %d %d", server_version, + server_version_split[0], + server_version_split[1], server_version_split[2])); +} + + /************************************************************************** Load_log_event methods General note about Load_log_event: the binlogging of LOAD DATA INFILE is @@ -2963,15 +3093,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;\n", + my_b_printf(&cache, "%suse %s%s\n", commented ? "# " : "", - db); + db, print_event_info->delimiter); if (flags & LOG_EVENT_THREAD_SPECIFIC_F) - my_b_printf(&cache,"%sSET @@session.pseudo_thread_id=%lu;\n", - commented ? "# " : "", (ulong)thread_id); + my_b_printf(&cache,"%sSET @@session.pseudo_thread_id=%lu%s\n", + commented ? "# " : "", (ulong)thread_id, + print_event_info->delimiter); my_b_printf(&cache, "%sLOAD DATA ", - commented ? "# " : ""); + commented ? "# " : ""); if (check_fname_outside_temp_buf()) my_b_printf(&cache, "LOCAL "); my_b_printf(&cache, "INFILE '%-*s' ", fname_len, fname); @@ -3021,7 +3152,7 @@ void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info, my_b_printf(&cache, ")"); } - my_b_printf(&cache, ";\n"); + my_b_printf(&cache, "%s\n", print_event_info->delimiter); DBUG_VOID_RETURN; } #endif /* MYSQL_CLIENT */ @@ -3244,10 +3375,9 @@ int Load_log_event::do_apply_event(NET* net, RELAY_LOG_INFO const *rli, ex.skip_lines = skip_lines; List<Item> field_list; - thd->main_lex.select_lex.context.resolve_in_table_list_only(&tables); - set_fields(tables.db, field_list, &thd->main_lex.select_lex.context); + thd->lex->select_lex.context.resolve_in_table_list_only(&tables); + set_fields(tables.db, field_list, &thd->lex->select_lex.context); thd->variables.pseudo_thread_id= thread_id; - List<Item> set_fields; if (net) { // mysql_load will use thd->net to read the file @@ -3258,10 +3388,11 @@ int Load_log_event::do_apply_event(NET* net, RELAY_LOG_INFO const *rli, thd->net.pkt_nr = net->pkt_nr; } /* - It is safe to use set_fields twice because we are not going to + It is safe to use tmp_list twice because we are not going to update it inside mysql_load(). */ - if (mysql_load(thd, &ex, &tables, field_list, set_fields, set_fields, + List<Item> tmp_list; + if (mysql_load(thd, &ex, &tables, field_list, tmp_list, tmp_list, handle_dup, ignore, net != 0)) thd->query_error= 1; if (thd->cuted_fields) @@ -3394,8 +3525,8 @@ Rotate_log_event::Rotate_log_event(const char* new_log_ident_arg, #ifndef DBUG_OFF char buff[22]; DBUG_ENTER("Rotate_log_event::Rotate_log_event(...,flags)"); - DBUG_PRINT("enter",("new_log_ident %s pos %s flags %lu", new_log_ident_arg, - llstr(pos_arg, buff), flags)); + DBUG_PRINT("enter",("new_log_ident: %s pos: %s flags: %lu", new_log_ident_arg, + llstr(pos_arg, buff), (ulong) flags)); #endif if (flags & DUP_NAME) new_log_ident= my_strndup(new_log_ident_arg, ident_len, MYF(MY_WME)); @@ -3653,7 +3784,8 @@ void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) msg="INVALID_INT"; break; } - my_b_printf(&cache, "%s=%s;\n", msg, llstr(val,llbuff)); + my_b_printf(&cache, "%s=%s%s\n", + msg, llstr(val,llbuff), print_event_info->delimiter); } #endif @@ -3755,8 +3887,9 @@ void Rand_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) print_header(&cache, print_event_info, FALSE); my_b_printf(&cache, "\tRand\n"); } - my_b_printf(&cache, "SET @@RAND_SEED1=%s, @@RAND_SEED2=%s;\n", - llstr(seed1, llbuff),llstr(seed2, llbuff2)); + my_b_printf(&cache, "SET @@RAND_SEED1=%s, @@RAND_SEED2=%s%s\n", + llstr(seed1, llbuff),llstr(seed2, llbuff2), + print_event_info->delimiter); } #endif /* MYSQL_CLIENT */ @@ -3853,7 +3986,7 @@ void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) print_header(&cache, print_event_info, FALSE); my_b_printf(&cache, "\tXid = %s\n", buf); } - my_b_printf(&cache, "COMMIT;\n"); + my_b_printf(&cache, "COMMIT%s\n", print_event_info->delimiter); } #endif /* MYSQL_CLIENT */ @@ -4058,7 +4191,7 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) if (is_null) { - my_b_printf(&cache, ":=NULL;\n"); + my_b_printf(&cache, ":=NULL%s\n", print_event_info->delimiter); } else { @@ -4066,12 +4199,12 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) case REAL_RESULT: double real_val; float8get(real_val, val); - my_b_printf(&cache, ":=%.14g;\n", real_val); + my_b_printf(&cache, ":=%.14g%s\n", real_val, print_event_info->delimiter); break; case INT_RESULT: char int_buf[22]; longlong10_to_str(uint8korr(val), int_buf, -10); - my_b_printf(&cache, ":=%s;\n", int_buf); + my_b_printf(&cache, ":=%s%s\n", int_buf, print_event_info->delimiter); break; case DECIMAL_RESULT: { @@ -4087,7 +4220,7 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) bin2decimal(val+2, &dec, precision, scale); decimal2string(&dec, str_buf, &str_len, 0, 0, 0); str_buf[str_len]= 0; - my_b_printf(&cache, ":=%s;\n",str_buf); + my_b_printf(&cache, ":=%s%s\n", str_buf, print_event_info->delimiter); break; } case STRING_RESULT: @@ -4123,9 +4256,11 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) Generate an unusable command (=> syntax error) is probably the best thing we can do here. */ - my_b_printf(&cache, ":=???;\n"); + my_b_printf(&cache, ":=???%s\n", print_event_info->delimiter); else - my_b_printf(&cache, ":=_%s %s COLLATE `%s`;\n", cs->csname, hex_str, cs->name); + my_b_printf(&cache, ":=_%s %s COLLATE `%s`%s\n", + cs->csname, hex_str, cs->name, + print_event_info->delimiter); my_afree(hex_str); } break; @@ -4293,7 +4428,7 @@ Slave_log_event::Slave_log_event(THD* thd_arg, memcpy(master_log, rli->group_master_log_name, master_log_len + 1); master_port = mi->port; master_pos = rli->group_master_log_pos; - DBUG_PRINT("info", ("master_log: %s pos: %d", master_log, + DBUG_PRINT("info", ("master_log: %s pos: %lu", master_log, (ulong) master_pos)); } else @@ -5251,12 +5386,12 @@ void Execute_load_query_log_event::print(FILE* file, my_b_printf(&cache, " REPLACE"); my_b_printf(&cache, " INTO"); my_b_write(&cache, (byte*) query + fn_pos_end, q_len-fn_pos_end); - my_b_printf(&cache, ";\n"); + my_b_printf(&cache, "%s\n", print_event_info->delimiter); } else { my_b_write(&cache, (byte*) query, q_len); - my_b_printf(&cache, ";\n"); + my_b_printf(&cache, "%s\n", print_event_info->delimiter); } if (!print_event_info->short_form) @@ -5430,8 +5565,6 @@ char* sql_ex_info::init(char* buf,char* buf_end,bool use_new_format) } -#ifdef HAVE_ROW_BASED_REPLICATION - /************************************************************************** Rows_log_event member functions **************************************************************************/ @@ -5487,8 +5620,8 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len, uint8 const common_header_len= description_event->common_header_len; uint8 const post_header_len= description_event->post_header_len[event_type-1]; - DBUG_PRINT("enter",("event_len=%ld, common_header_len=%d, " - "post_header_len=%d", + DBUG_PRINT("enter",("event_len: %u common_header_len: %d " + "post_header_len: %d", event_len, common_header_len, post_header_len)); @@ -5502,7 +5635,7 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len, } else { - m_table_id= uint6korr(post_start); + m_table_id= (ulong) uint6korr(post_start); post_start+= RW_FLAGS_OFFSET; } @@ -5518,7 +5651,7 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len, const byte* const ptr_rows_data= var_start + byte_count + 1; my_size_t const data_size= event_len - (ptr_rows_data - (const byte *) buf); - DBUG_PRINT("info",("m_table_id=%lu, m_flags=%d, m_width=%u, data_size=%lu", + DBUG_PRINT("info",("m_table_id: %lu m_flags: %d m_width: %lu data_size: %lu", m_table_id, m_flags, m_width, data_size)); m_rows_buf= (byte*)my_malloc(data_size, MYF(MY_WME)); @@ -5559,23 +5692,28 @@ int Rows_log_event::do_add_row_data(byte *const row_data, */ DBUG_ENTER("Rows_log_event::do_add_row_data"); DBUG_PRINT("enter", ("row_data: 0x%lx length: %lu", (ulong) row_data, - length)); + (ulong) length)); + /* + Don't print debug messages when running valgrind since they can + trigger false warnings. + */ +#ifndef HAVE_purify DBUG_DUMP("row_data", (const char*)row_data, min(length, 32)); +#endif DBUG_ASSERT(m_rows_buf <= m_rows_cur); DBUG_ASSERT(!m_rows_buf || m_rows_end && m_rows_buf < m_rows_end); DBUG_ASSERT(m_rows_cur <= m_rows_end); /* The cast will always work since m_rows_cur <= m_rows_end */ - if (static_cast<my_size_t>(m_rows_end - m_rows_cur) < length) + if (static_cast<my_size_t>(m_rows_end - m_rows_cur) <= length) { my_size_t const block_size= 1024; - my_ptrdiff_t const old_alloc= m_rows_end - m_rows_buf; my_ptrdiff_t const cur_size= m_rows_cur - m_rows_buf; my_ptrdiff_t const new_alloc= - block_size * ((cur_size + length) / block_size + block_size - 1); + block_size * ((cur_size + length + block_size - 1) / block_size); - byte* const new_buf= (byte*)my_realloc((gptr)m_rows_buf, new_alloc, + byte* const new_buf= (byte*)my_realloc((gptr)m_rows_buf, (uint) new_alloc, MYF(MY_ALLOW_ZERO_PTR|MY_WME)); if (unlikely(!new_buf)) DBUG_RETURN(HA_ERR_OUT_OF_MEM); @@ -5594,7 +5732,7 @@ int Rows_log_event::do_add_row_data(byte *const row_data, m_rows_end= m_rows_buf + new_alloc; } - DBUG_ASSERT(m_rows_cur + length < m_rows_end); + DBUG_ASSERT(m_rows_cur + length <= m_rows_end); memcpy(m_rows_cur, row_data, length); m_rows_cur+= length; m_row_count++; @@ -5604,14 +5742,13 @@ int Rows_log_event::do_add_row_data(byte *const row_data, #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) /* - Unpack a row into a record. + Unpack a row into table->record[0]. SYNOPSIS unpack_row() rli Relay log info table Table to unpack into colcnt Number of columns to read from record - record Record where the data should be unpacked row Packed row data cols Pointer to columns data to fill in row_end Pointer to variable that will hold the value of the @@ -5624,6 +5761,11 @@ int Rows_log_event::do_add_row_data(byte *const row_data, DESCRIPTION + The function will always unpack into the table->record[0] + record. This is because there are too many dependencies on + where the various member functions of Field and subclasses + expect to write. + The row is assumed to only consist of the fields for which the bitset represented by 'arr' and 'bits'; the other parts of the record are left alone. @@ -5642,13 +5784,15 @@ int Rows_log_event::do_add_row_data(byte *const row_data, */ static int unpack_row(RELAY_LOG_INFO const *rli, - TABLE *table, uint const colcnt, byte *record, + TABLE *table, uint const colcnt, char const *row, MY_BITMAP const *cols, char const **row_end, ulong *master_reclength, MY_BITMAP* const rw_set, Log_event_type const event_type) { + byte *const record= table->record[0]; + DBUG_ENTER("unpack_row"); DBUG_ASSERT(record && row); - my_ptrdiff_t const offset= record - (byte*) table->record[0]; + DBUG_PRINT("enter", ("row: 0x%lx table->record[0]: 0x%lx", (long) row, (long) record)); my_size_t master_null_bytes= table->s->null_bytes; if (colcnt != table->s->fields) @@ -5688,9 +5832,13 @@ unpack_row(RELAY_LOG_INFO const *rli, if (bitmap_is_set(cols, field_ptr - begin_ptr)) { - f->move_field_offset(offset); + DBUG_ASSERT((const char *)table->record[0] <= f->ptr); + DBUG_ASSERT(f->ptr < (char*)(table->record[0] + table->s->reclength + + (f->pack_length_in_rec() == 0))); + + DBUG_PRINT("info", ("unpacking column '%s' to 0x%lx", f->field_name, + (long) f->ptr)); ptr= f->unpack(f->ptr, ptr); - f->move_field_offset(-offset); /* Field...::unpack() cannot return 0 */ DBUG_ASSERT(ptr != NULL); } @@ -5721,13 +5869,11 @@ unpack_row(RELAY_LOG_INFO const *rli, for ( ; *field_ptr ; ++field_ptr) { uint32 const mask= NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG; + Field *const f= *field_ptr; - DBUG_PRINT("debug", ("flags = 0x%x, mask = 0x%x, flags & mask = 0x%x", - (*field_ptr)->flags, mask, - (*field_ptr)->flags & mask)); - - if (event_type == WRITE_ROWS_EVENT && - ((*field_ptr)->flags & mask) == mask) + DBUG_PRINT("info", ("processing column '%s' @ 0x%lx", f->field_name, + (long) f->ptr)); + if (event_type == WRITE_ROWS_EVENT && (f->flags & mask) == mask) { slave_print_msg(ERROR_LEVEL, rli, ER_NO_DEFAULT_FOR_FIELD, "Field `%s` of table `%s`.`%s` " @@ -5737,10 +5883,10 @@ unpack_row(RELAY_LOG_INFO const *rli, error = ER_NO_DEFAULT_FOR_FIELD; } else - (*field_ptr)->set_default(); + f->set_default(); } - return error; + DBUG_RETURN(error); } int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli) @@ -5799,9 +5945,26 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli) { if (!need_reopen) { - slave_print_msg(ERROR_LEVEL, rli, error, - "Error in %s event: when locking tables", - get_type_str()); + if (thd->query_error || thd->is_fatal_error) + { + /* + Error reporting borrowed from Query_log_event with many excessive + simplifications (we don't honour --slave-skip-errors) + */ + uint actual_error= thd->net.last_errno; + slave_print_msg(ERROR_LEVEL, rli, actual_error, + "Error '%s' in %s event: when locking tables", + (actual_error ? thd->net.last_error : + "unexpected success or fatal error"), + get_type_str()); + thd->is_fatal_error= 1; + } + else + { + slave_print_msg(ERROR_LEVEL, rli, error, + "Error in %s event: when locking tables", + get_type_str()); + } const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock(); DBUG_RETURN(error); } @@ -5820,12 +5983,11 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli) need to add code to assert that is the case. */ thd->binlog_flush_pending_rows_event(false); - close_tables_for_reopen(thd, &const_cast<RELAY_LOG_INFO*>(rli)->tables_to_lock); + TABLE_LIST *tables= rli->tables_to_lock; + close_tables_for_reopen(thd, &tables); - if ((error= open_tables(thd, - &const_cast<RELAY_LOG_INFO*>(rli)->tables_to_lock, - &const_cast<RELAY_LOG_INFO*>(rli)->tables_to_lock_count, - 0))) + uint tables_count= rli->tables_to_lock_count; + if ((error= open_tables(thd, &tables, &tables_count, 0))) { if (thd->query_error || thd->is_fatal_error) { @@ -5844,16 +6006,45 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli) DBUG_RETURN(error); } } + + /* + When the open and locking succeeded, we check all tables to + ensure that they still have the correct type. + + We can use a down cast here since we know that every table added + to the tables_to_lock is a RPL_TABLE_LIST. + */ + + { + RPL_TABLE_LIST *ptr= rli->tables_to_lock; + for ( ; ptr ; ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_global)) + { + if (ptr->m_tabledef.compatible_with(rli, ptr->table)) + { + mysql_unlock_tables(thd, thd->lock); + thd->lock= 0; + thd->query_error= 1; + rli->clear_tables_to_lock(); + DBUG_RETURN(ERR_BAD_TABLE_DEF); + } + } + } + /* - When the open and locking succeeded, we add all the tables to - the table map and remove them from tables to lock. + ... and then we add all the tables to the table map and remove + them from tables to lock. We also invalidate the query cache for all the tables, since they will now be changed. + + TODO [/Matz]: Maybe the query cache should not be invalidated + here? It might be that a table is not changed, even though it + was locked for the statement. We do know that each + Rows_log_event contain at least one row, so after processing one + Rows_log_event, we can invalidate the query cache for the + associated table. */ - - TABLE_LIST *ptr; - for (ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global) + for (TABLE_LIST *ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global) { const_cast<RELAY_LOG_INFO*>(rli)->m_table_map.set_table(ptr->table_id, ptr->table); } @@ -5908,7 +6099,7 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli) if ((error= do_prepare_row(thd, rli, table, row_start, &row_end))) break; // We should perform the after-row operation even in // the case of error - + DBUG_ASSERT(row_end != NULL); // cannot happen DBUG_ASSERT(row_end <= (const char*)m_rows_end); @@ -5965,6 +6156,7 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli) STMT_END_F. For now we code, knowing that error is not skippable and so slave SQL thread is certainly going to stop. + rollback at the caller along with sbr. */ thd->reset_current_stmt_binlog_row_based(); const_cast<RELAY_LOG_INFO*>(rli)->cleanup_context(thd, error); @@ -6095,7 +6287,7 @@ bool Rows_log_event::write_data_body(IO_CACHE*file) sbuf_end - sbuf) || my_b_safe_write(file, reinterpret_cast<byte*>(m_cols.bitmap), no_bytes_in_map(&m_cols)) || - my_b_safe_write(file, m_rows_buf, data_size)); + my_b_safe_write(file, m_rows_buf, (uint) data_size)); } #endif @@ -6122,7 +6314,7 @@ void Rows_log_event::print_helper(FILE *file, { bool const last_stmt_event= get_flags(STMT_END_F); print_header(head, print_event_info, !last_stmt_event); - my_b_printf(head, "\t%s: table id %lu", name, m_table_id); + my_b_printf(head, "\t%s: table id %lu\n", name, m_table_id); print_base64(body, print_event_info, !last_stmt_event); } @@ -6204,10 +6396,16 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len, uint8 common_header_len= description_event->common_header_len; uint8 post_header_len= description_event->post_header_len[TABLE_MAP_EVENT-1]; - DBUG_PRINT("info",("event_len=%ld, common_header_len=%d, post_header_len=%d", + DBUG_PRINT("info",("event_len: %u common_header_len: %d post_header_len: %d", event_len, common_header_len, post_header_len)); + /* + Don't print debug messages when running valgrind since they can + trigger false warnings. + */ +#ifndef HAVE_purify DBUG_DUMP("event buffer", buf, event_len); +#endif /* Read the post-header */ const char *post_start= buf + common_header_len; @@ -6222,7 +6420,7 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len, else { DBUG_ASSERT(post_header_len == TABLE_MAP_HEADER_LEN); - m_table_id= uint6korr(post_start); + m_table_id= (ulong) uint6korr(post_start); post_start+= TM_FLAGS_OFFSET; } @@ -6246,10 +6444,10 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len, uchar *ptr_after_colcnt= (uchar*) ptr_colcnt; m_colcnt= net_field_length(&ptr_after_colcnt); - DBUG_PRINT("info",("m_dblen=%d off=%d m_tbllen=%d off=%d m_colcnt=%d off=%d", - m_dblen, ptr_dblen-(const byte*)vpart, - m_tbllen, ptr_tbllen-(const byte*)vpart, - m_colcnt, ptr_colcnt-(const byte*)vpart)); + DBUG_PRINT("info",("m_dblen: %lu off: %ld m_tbllen: %lu off: %ld m_colcnt: %lu off: %ld", + m_dblen, (long) (ptr_dblen-(const byte*)vpart), + m_tbllen, (long) (ptr_tbllen-(const byte*)vpart), + m_colcnt, (long) (ptr_colcnt-(const byte*)vpart))); /* Allocate mem for all fields in one go. If fails, catched in is_valid() */ m_memory= my_multi_malloc(MYF(MY_WME), @@ -6298,11 +6496,11 @@ int Table_map_log_event::do_apply_event(RELAY_LOG_INFO const *rli) thd->query_id= next_query_id(); pthread_mutex_unlock(&LOCK_thread_count); - TABLE_LIST *table_list; + RPL_TABLE_LIST *table_list; char *db_mem, *tname_mem; void *const memory= my_multi_malloc(MYF(MY_WME), - &table_list, sizeof(TABLE_LIST), + &table_list, sizeof(RPL_TABLE_LIST), &db_mem, NAME_LEN + 1, &tname_mem, NAME_LEN + 1, NULL); @@ -6348,11 +6546,27 @@ int Table_map_log_event::do_apply_event(RELAY_LOG_INFO const *rli) } /* - Open the table if it is not already open and add the table to table map. - Note that for any table that should not be replicated, a filter is needed. + Open the table if it is not already open and add the table to + table map. Note that for any table that should not be + replicated, a filter is needed. + + The creation of a new TABLE_LIST is used to up-cast the + table_list consisting of RPL_TABLE_LIST items. This will work + since the only case where the argument to open_tables() is + changed, is when thd->lex->query_tables == table_list, i.e., + when the statement requires prelocking. Since this is not + executed when a statement is executed, this case will not occur. + As a precaution, an assertion is added to ensure that the bad + case is not a fact. + + Either way, the memory in the list is *never* released + internally in the open_tables() function, hence we take a copy + of the pointer to make sure that it's not lost. */ uint count; - if ((error= open_tables(thd, &table_list, &count, 0))) + DBUG_ASSERT(thd->lex->query_tables != table_list); + TABLE_LIST *tmp_table_list= table_list; + if ((error= open_tables(thd, &tmp_table_list, &count, 0))) { if (thd->query_error || thd->is_fatal_error) { @@ -6379,14 +6593,17 @@ int Table_map_log_event::do_apply_event(RELAY_LOG_INFO const *rli) */ DBUG_ASSERT(m_table->in_use); - table_def const def(m_coltype, m_colcnt); - if (def.compatible_with(rli, m_table)) - { - thd->query_error= 1; - error= ERR_BAD_TABLE_DEF; - goto err; - /* purecov: end */ - } + /* + Use placement new to construct the table_def instance in the + memory allocated for it inside table_list. + + The memory allocated by the table_def structure (i.e., not the + memory allocated *for* the table_def structure) is released + inside st_relay_log_info::clear_tables_to_lock() by calling the + table_def destructor explicitly. + */ + new (&table_list->m_tabledef) table_def(m_coltype, m_colcnt); + table_list->m_tabledef_valid= TRUE; /* We record in the slave's information that the table should be @@ -6437,8 +6654,8 @@ bool Table_map_log_event::write_data_body(IO_CACHE *file) DBUG_ASSERT(m_dblen < 128); DBUG_ASSERT(m_tbllen < 128); - byte const dbuf[]= { m_dblen }; - byte const tbuf[]= { m_tbllen }; + byte const dbuf[]= { (byte) m_dblen }; + byte const tbuf[]= { (byte) m_tbllen }; char cbuf[sizeof(m_colcnt)]; char *const cbuf_end= net_store_length((char*) cbuf, (uint) m_colcnt); @@ -6577,17 +6794,15 @@ int Write_rows_log_event::do_after_row_operations(TABLE *table, int error) int Write_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO const *rli, TABLE *table, - char const *row_start, - char const **row_end) + char const *const row_start, + char const **const row_end) { DBUG_ASSERT(table != NULL); DBUG_ASSERT(row_start && row_end); int error; - error= unpack_row(rli, - table, m_width, table->record[0], - row_start, &m_cols, row_end, &m_master_reclength, - table->write_set, WRITE_ROWS_EVENT); + error= unpack_row(rli, table, m_width, row_start, &m_cols, row_end, + &m_master_reclength, table->write_set, WRITE_ROWS_EVENT); bitmap_copy(table->read_set, table->write_set); return error; } @@ -6648,11 +6863,11 @@ copy_extra_record_fields(TABLE *table, my_size_t master_reclength, my_ptrdiff_t master_fields) { - DBUG_PRINT("info", ("Copying to %p " - "from field %d at offset %u " - "to field %d at offset %u", - table->record[0], - master_fields, master_reclength, + DBUG_PRINT("info", ("Copying to 0x%lx " + "from field %lu at offset %lu " + "to field %d at offset %lu", + (long) table->record[0], + (ulong) master_fields, (ulong) master_reclength, table->s->fields, table->s->reclength)); /* Copying the extra fields of the slave that does not exist on @@ -6702,12 +6917,15 @@ copy_extra_record_fields(TABLE *table, /* Nothing to do */ break; - case FIELD_TYPE_BIT: + case MYSQL_TYPE_BIT: Field_bit *f= static_cast<Field_bit*>(*field_ptr); - my_ptrdiff_t const offset= table->record[1] - table->record[0]; - uchar const bits= - get_rec_bits(f->bit_ptr + offset, f->bit_ofs, f->bit_len); - set_rec_bits(bits, f->bit_ptr, f->bit_ofs, f->bit_len); + if (f->bit_len > 0) + { + my_ptrdiff_t const offset= table->record[1] - table->record[0]; + uchar const bits= + get_rec_bits(f->bit_ptr + offset, f->bit_ofs, f->bit_len); + set_rec_bits(bits, f->bit_ptr, f->bit_ofs, f->bit_len); + } break; } } @@ -6775,9 +6993,9 @@ replace_record(THD *thd, TABLE *table, while ((error= table->file->ha_write_row(table->record[0]))) { - if (!is_duplicate_key_error(error)) + if (error == HA_ERR_LOCK_DEADLOCK || error == HA_ERR_LOCK_WAIT_TIMEOUT) { - table->file->print_error(error, MYF(0)); + table->file->print_error(error, MYF(0)); /* to check at exec_relay_log_event */ DBUG_RETURN(error); } if ((keynum= table->file->get_dup_key(error)) < 0) @@ -6840,7 +7058,7 @@ replace_record(THD *thd, TABLE *table, present on the master from table->record[1], if there are any. */ copy_extra_record_fields(table, master_reclength, master_fields); - + /* REPLACE is defined as either INSERT or DELETE + INSERT. If possible, we can replace it with an UPDATE, but that will not @@ -6925,8 +7143,26 @@ static bool record_compare(TABLE *table) /* Find the row given by 'key', if the table has keys, or else use a table scan - to find (and fetch) the row. If the engine allows random access of the - records, a combination of position() and rnd_pos() will be used. + to find (and fetch) the row. + + If the engine allows random access of the records, a combination of + position() and rnd_pos() will be used. + + @param table Pointer to table to search + @param key Pointer to key to use for search, if table has key + + @pre <code>table->record[0]</code> shall contain the row to locate + and <code>key</code> shall contain a key to use for searching, if + the engine has a key. + + @post If the return value is zero, <code>table->record[1]</code> + will contain the fetched row and the internal "cursor" will refer to + the row. If the return value is non-zero, + <code>table->record[1]</code> is undefined. In either case, + <code>table->record[0]</code> is undefined. + + @return Zero if the row was successfully fetched into + <code>table->record[1]</code>, error code otherwise. */ static int find_and_fetch_row(TABLE *table, byte *key) @@ -6946,13 +7182,28 @@ static int find_and_fetch_row(TABLE *table, byte *key) row reference using the position() member function (it will be stored in table->file->ref) and the use rnd_pos() to position the "cursor" (i.e., record[0] in this case) at the correct row. + + TODO: Add a check that the correct record has been fetched by + comparing with the original record. Take into account that the + record on the master and slave can be of different + length. Something along these lines should work: + + ADD>>> store_record(table,record[1]); + int error= table->file->rnd_pos(table->record[0], table->file->ref); + ADD>>> DBUG_ASSERT(memcmp(table->record[1], table->record[0], + table->s->reclength) == 0); + */ table->file->position(table->record[0]); - DBUG_RETURN(table->file->rnd_pos(table->record[0], table->file->ref)); + int error= table->file->rnd_pos(table->record[0], table->file->ref); + /* + rnd_pos() returns the record in table->record[0], so we have to + move it to table->record[1]. + */ + bmove_align(table->record[1], table->record[0], table->s->reclength); + DBUG_RETURN(error); } - DBUG_ASSERT(table->record[1]); - /* We need to retrieve all fields */ /* TODO: Move this out from this function to main loop */ table->use_all_columns(); @@ -6962,7 +7213,16 @@ static int find_and_fetch_row(TABLE *table, byte *key) int error; /* We have a key: search the table using the index */ if (!table->file->inited && (error= table->file->ha_index_init(0, FALSE))) - return error; + DBUG_RETURN(error); + + /* + Don't print debug messages when running valgrind since they can + trigger false warnings. + */ +#ifndef HAVE_purify + DBUG_DUMP("table->record[0]", (const char *)table->record[0], table->s->reclength); + DBUG_DUMP("table->record[1]", (const char *)table->record[1], table->s->reclength); +#endif /* We need to set the null bytes to ensure that the filler bit are @@ -6982,6 +7242,14 @@ static int find_and_fetch_row(TABLE *table, byte *key) DBUG_RETURN(error); } + /* + Don't print debug messages when running valgrind since they can + trigger false warnings. + */ +#ifndef HAVE_purify + DBUG_DUMP("table->record[0]", (const char *)table->record[0], table->s->reclength); + DBUG_DUMP("table->record[1]", (const char *)table->record[1], table->s->reclength); +#endif /* Below is a minor "optimization". If the key (i.e., key number 0) has the HA_NOSAME flag set, we know that we have found the @@ -7166,8 +7434,8 @@ int Delete_rows_log_event::do_after_row_operations(TABLE *table, int error) int Delete_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO const *rli, TABLE *table, - char const *row_start, - char const **row_end) + char const *const row_start, + char const **const row_end) { int error; DBUG_ASSERT(row_start && row_end); @@ -7177,10 +7445,8 @@ int Delete_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO const *rli, */ DBUG_ASSERT(table->s->fields >= m_width); - error= unpack_row(rli, - table, m_width, table->record[0], - row_start, &m_cols, row_end, &m_master_reclength, - table->read_set, DELETE_ROWS_EVENT); + error= unpack_row(rli, table, m_width, row_start, &m_cols, row_end, + &m_master_reclength, table->read_set, DELETE_ROWS_EVENT); /* If we will access rows using the random access method, m_key will be set to NULL, so we do not need to make a key copy in that case. @@ -7303,8 +7569,8 @@ int Update_rows_log_event::do_after_row_operations(TABLE *table, int error) int Update_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO const *rli, TABLE *table, - char const *row_start, - char const **row_end) + char const *const row_start, + char const **const row_end) { int error; DBUG_ASSERT(row_start && row_end); @@ -7314,21 +7580,31 @@ int Update_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO const *rli, */ DBUG_ASSERT(table->s->fields >= m_width); + /* + We need to perform some juggling below since unpack_row() always + unpacks into table->record[0]. For more information, see the + comments for unpack_row(). + */ + /* record[0] is the before image for the update */ - error= unpack_row(rli, - table, m_width, table->record[0], - row_start, &m_cols, row_end, &m_master_reclength, - table->read_set, UPDATE_ROWS_EVENT); - row_start = *row_end; + error= unpack_row(rli, table, m_width, row_start, &m_cols, row_end, + &m_master_reclength, table->read_set, UPDATE_ROWS_EVENT); + store_record(table, record[1]); + char const *next_start = *row_end; /* m_after_image is the after image for the update */ - error= unpack_row(rli, - table, m_width, m_after_image, - row_start, &m_cols, row_end, &m_master_reclength, - table->write_set, UPDATE_ROWS_EVENT); - - DBUG_DUMP("record[0]", (char *)table->record[0], table->s->reclength); - DBUG_DUMP("m_after_image", (char *)m_after_image, table->s->reclength); + error= unpack_row(rli, table, m_width, next_start, &m_cols, row_end, + &m_master_reclength, table->write_set, UPDATE_ROWS_EVENT); + bmove_align(m_after_image, table->record[0], table->s->reclength); + restore_record(table, record[1]); + /* + Don't print debug messages when running valgrind since they can + trigger false warnings. + */ +#ifndef HAVE_purify + DBUG_DUMP("record[0]", (const char *)table->record[0], table->s->reclength); + DBUG_DUMP("m_after_image", (const char *)m_after_image, table->s->reclength); +#endif /* If we will access rows using the random access method, m_key will @@ -7388,4 +7664,3 @@ void Update_rows_log_event::print(FILE *file, } #endif -#endif /* defined(HAVE_ROW_BASED_REPLICATION) */ diff --git a/sql/log_event.h b/sql/log_event.h index 8da02a77b50..7cd231a8353 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -200,8 +199,26 @@ struct sql_ex_info #define EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN (4 + 4 + 4 + 1) #define EXECUTE_LOAD_QUERY_HEADER_LEN (QUERY_HEADER_LEN + EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN) -/* - Event header offsets; +/* + Max number of possible extra bytes in a replication event compared to a + packet (i.e. a query) sent from client to master; + First, an auxiliary log_event status vars estimation: +*/ +#define MAX_SIZE_LOG_EVENT_STATUS (4 /* flags2 */ + \ + 8 /* sql mode */ + \ + 1 + 1 + 255 /* catalog */ + \ + 4 /* autoinc */ + \ + 6 /* charset */ + \ + MAX_TIME_ZONE_NAME_LENGTH) +#define MAX_LOG_EVENT_HEADER ( /* in order of Query_log_event::write */ \ + LOG_EVENT_HEADER_LEN + /* write_header */ \ + QUERY_HEADER_LEN + /* write_data */ \ + EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN + /*write_post_header_for_derived */ \ + MAX_SIZE_LOG_EVENT_STATUS + /* status */ \ + NAME_LEN + 1) + +/* + Event header offsets; these point to places inside the fixed header. */ @@ -253,6 +270,9 @@ struct sql_ex_info */ #define Q_CATALOG_NZ_CODE 6 +#define Q_LC_TIME_NAMES_CODE 7 + +#define Q_CHARSET_DATABASE_CODE 8 /* Intvar event post-header */ #define I_TYPE_OFFSET 0 @@ -405,12 +425,18 @@ struct sql_ex_info either, as the manual says (because a too big in-memory temp table is automatically written to disk). */ -#define OPTIONS_WRITTEN_TO_BIN_LOG (OPTION_AUTO_IS_NULL | \ -OPTION_NO_FOREIGN_KEY_CHECKS | OPTION_RELAXED_UNIQUE_CHECKS) +#define OPTIONS_WRITTEN_TO_BIN_LOG \ + (OPTION_AUTO_IS_NULL | OPTION_NO_FOREIGN_KEY_CHECKS | \ + OPTION_RELAXED_UNIQUE_CHECKS | OPTION_NOT_AUTOCOMMIT) -#if OPTIONS_WRITTEN_TO_BIN_LOG != ((1L << 14) | (1L << 26) | (1L << 27)) +/* Shouldn't be defined before */ +#define EXPECTED_OPTIONS \ + ((ULL(1) << 14) | (ULL(1) << 26) | (ULL(1) << 27) | (ULL(1) << 19)) + +#if OPTIONS_WRITTEN_TO_BIN_LOG != EXPECTED_OPTIONS #error OPTIONS_WRITTEN_TO_BIN_LOG must NOT change their values! #endif +#undef EXPECTED_OPTIONS /* You shouldn't use this one */ enum Log_event_type { @@ -508,9 +534,12 @@ typedef struct st_print_event_info bool charset_inited; char charset[6]; // 3 variables, each of them storable in 2 bytes char time_zone_str[MAX_TIME_ZONE_NAME_LENGTH]; + uint lc_time_names_number; + uint charset_database_number; st_print_event_info() :flags2_inited(0), sql_mode_inited(0), - auto_increment_increment(1),auto_increment_offset(1), charset_inited(0) + auto_increment_increment(1),auto_increment_offset(1), charset_inited(0), + lc_time_names_number(0), charset_database_number(0) { /* Currently we only use static PRINT_EVENT_INFO objects, so zeroed at @@ -520,15 +549,19 @@ typedef struct st_print_event_info bzero(db, sizeof(db)); bzero(charset, sizeof(charset)); bzero(time_zone_str, sizeof(time_zone_str)); - uint const flags = MYF(MY_WME | MY_NABP); - init_io_cache(&head_cache, -1, 0, WRITE_CACHE, 0L, FALSE, flags); - init_io_cache(&body_cache, -1, 0, WRITE_CACHE, 0L, FALSE, flags); + delimiter[0]= ';'; + delimiter[1]= 0; + myf const flags = MYF(MY_WME | MY_NABP); + open_cached_file(&head_cache, NULL, NULL, 0, flags); + open_cached_file(&body_cache, NULL, NULL, 0, flags); } ~st_print_event_info() { - end_io_cache(&head_cache); - end_io_cache(&body_cache); + close_cached_file(&head_cache); + close_cached_file(&body_cache); } + bool init_ok() /* tells if construction was successful */ + { return my_b_inited(&head_cache) && my_b_inited(&body_cache); } /* Settings on how to print the events */ @@ -536,6 +569,7 @@ typedef struct st_print_event_info bool base64_output; my_off_t hexdump_from; uint8 common_header_len; + char delimiter[16]; /* These two caches are used by the row-based replication events to @@ -586,6 +620,13 @@ public: /* + The following type definition is to be used whenever data is placed + and manipulated in a common buffer. Use this typedef for buffers + that contain data containing binary and character data. + */ + typedef unsigned char Byte; + + /* The offset in the log where this event originally appeared (it is preserved in relay logs, making SHOW SLAVE STATUS able to print coordinates of the event in the master's binlog). Note: when a @@ -885,7 +926,7 @@ protected: class Query_log_event: public Log_event { protected: - char* data_buf; + Log_event::Byte* data_buf; public: const char* query; const char* catalog; @@ -956,6 +997,8 @@ public: char charset[6]; uint time_zone_len; /* 0 means uninited */ const char *time_zone_str; + uint lc_time_names_number; /* 0 means en_US */ + uint charset_database_number; #ifndef MYSQL_CLIENT @@ -1020,6 +1063,8 @@ public: bool write(IO_CACHE* file) { return(false); }; virtual bool write_post_header_for_derived(IO_CACHE* file) { return FALSE; } +#else + Muted_query_log_event() {} #endif }; @@ -1292,6 +1337,7 @@ public: uint8 number_of_event_types; /* The list of post-headers' lengthes */ uint8 *post_header_len; + uchar server_version_split[3]; Format_description_log_event(uint8 binlog_ver, const char* server_ver=0); Format_description_log_event(const char* buf, uint event_len, @@ -1317,6 +1363,8 @@ public: return FORMAT_DESCRIPTION_HEADER_LEN; } + void calc_server_version_split(); + protected: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) virtual int do_apply_event(RELAY_LOG_INFO const *rli); @@ -1911,8 +1959,6 @@ public: #endif char *str_to_hex(char *to, const char *from, uint len); -#ifdef HAVE_ROW_BASED_REPLICATION - /***************************************************************************** Table map log event class @@ -1930,14 +1976,17 @@ public: TYPE_CODE = TABLE_MAP_EVENT }; + /** + Enumeration of the errors that can be returned. + */ enum enum_error { - ERR_OPEN_FAILURE = -1, /* Failure to open table */ - ERR_OK = 0, /* No error */ - ERR_TABLE_LIMIT_EXCEEDED = 1, /* No more room for tables */ - ERR_OUT_OF_MEM = 2, /* Out of memory */ - ERR_BAD_TABLE_DEF = 3, /* Table definition does not match */ - ERR_RBR_TO_SBR = 4 /* daisy-chanining RBR to SBR not allowed */ + ERR_OPEN_FAILURE = -1, /**< Failure to open table */ + ERR_OK = 0, /**< No error */ + ERR_TABLE_LIMIT_EXCEEDED = 1, /**< No more room for tables */ + ERR_OUT_OF_MEM = 2, /**< Out of memory */ + ERR_BAD_TABLE_DEF = 3, /**< Table definition does not match */ + ERR_RBR_TO_SBR = 4 /**< daisy-chanining RBR to SBR not allowed */ }; enum enum_flag @@ -2021,7 +2070,7 @@ private: Row level log event class. - Common base class for all row-level log events. + Common base class for all row-containing log events. RESPONSIBILITIES @@ -2035,6 +2084,19 @@ private: class Rows_log_event : public Log_event { public: + /** + Enumeration of the errors that can be returned. + */ + enum enum_error + { + ERR_OPEN_FAILURE = -1, /**< Failure to open table */ + ERR_OK = 0, /**< No error */ + ERR_TABLE_LIMIT_EXCEEDED = 1, /**< No more room for tables */ + ERR_OUT_OF_MEM = 2, /**< Out of memory */ + ERR_BAD_TABLE_DEF = 3, /**< Table definition does not match */ + ERR_RBR_TO_SBR = 4 /**< daisy-chanining RBR to SBR not allowed */ + }; + /* These definitions allow you to combine the flags into an appropriate flag set using the normal bitwise operators. The @@ -2042,7 +2104,6 @@ public: accepted by the compiler, which is then used to set the real set of flags. */ - enum enum_flag { /* Last event of a statement */ @@ -2246,7 +2307,7 @@ public: Write_rows_log_event(const char *buf, uint event_len, const Format_description_log_event *description_event); #endif -#if !defined(MYSQL_CLIENT) && defined(HAVE_ROW_BASED_REPLICATION) +#if !defined(MYSQL_CLIENT) static bool binlog_row_logging_function(THD *thd, TABLE *table, bool is_transactional, MY_BITMAP *cols, @@ -2311,7 +2372,7 @@ public: const Format_description_log_event *description_event); #endif -#if !defined(MYSQL_CLIENT) && defined(HAVE_ROW_BASED_REPLICATION) +#if !defined(MYSQL_CLIENT) static bool binlog_row_logging_function(THD *thd, TABLE *table, bool is_transactional, MY_BITMAP *cols, @@ -2381,7 +2442,7 @@ public: Delete_rows_log_event(const char *buf, uint event_len, const Format_description_log_event *description_event); #endif -#if !defined(MYSQL_CLIENT) && defined(HAVE_ROW_BASED_REPLICATION) +#if !defined(MYSQL_CLIENT) static bool binlog_row_logging_function(THD *thd, TABLE *table, bool is_transactional, MY_BITMAP *cols, @@ -2415,6 +2476,4 @@ private: #endif }; -#endif /* HAVE_ROW_BASED_REPLICATION */ - #endif /* _log_event_h */ diff --git a/sql/matherr.c b/sql/matherr.c index ea0c15d2feb..4998d8b4961 100644 --- a/sql/matherr.c +++ b/sql/matherr.c @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2001 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/mf_iocache.cc b/sql/mf_iocache.cc index 71c8d588de7..f237f15dbc9 100644 --- a/sql/mf_iocache.cc +++ b/sql/mf_iocache.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2004 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/my_decimal.cc b/sql/my_decimal.cc index 89607129026..511942e9a5d 100644 --- a/sql/my_decimal.cc +++ b/sql/my_decimal.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2004 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2005-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -15,6 +14,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mysql_priv.h" +#include <time.h> + #ifndef MYSQL_CLIENT /* @@ -190,6 +191,23 @@ int str2my_decimal(uint mask, const char *from, uint length, } +my_decimal *date2my_decimal(TIME *ltime, my_decimal *dec) +{ + longlong date; + date = (ltime->year*100L + ltime->month)*100L + ltime->day; + if (ltime->time_type > MYSQL_TIMESTAMP_DATE) + date= ((date*100L + ltime->hour)*100L+ ltime->minute)*100L + ltime->second; + if (int2my_decimal(E_DEC_FATAL_ERROR, date, FALSE, dec)) + return dec; + if (ltime->second_part) + { + dec->buf[(dec->intg-1) / 9 + 1]= ltime->second_part * 1000; + dec->frac= 6; + } + return dec; +} + + #ifndef DBUG_OFF /* routines for debugging print */ diff --git a/sql/my_decimal.h b/sql/my_decimal.h index 3ce8cfee75d..cefc5ee00fd 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2004 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2005-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -295,7 +294,12 @@ int string2my_decimal(uint mask, const String *str, my_decimal *d) { return str2my_decimal(mask, str->ptr(), str->length(), str->charset(), d); } -#endif + + +my_decimal *date2my_decimal(TIME *ltime, my_decimal *dec); + + +#endif /*defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY) */ inline int double2my_decimal(uint mask, double val, my_decimal *d) diff --git a/sql/my_lock.c b/sql/my_lock.c index 69884df22f8..f66d7282f72 100644 --- a/sql/my_lock.c +++ b/sql/my_lock.c @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2003 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index b7ef2cd97df..efcbdf968bf 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -36,8 +35,10 @@ #include <signal.h> #include <thr_lock.h> #include <my_base.h> /* Needed by field.h */ +#include <queues.h> #include "sql_bitmap.h" #include "sql_array.h" +#include "scheduler.h" /* TODO convert all these three maps to Bitmap classes */ typedef ulonglong table_map; /* Used for table bits in join */ @@ -57,10 +58,10 @@ typedef ulonglong nested_join_map; /* query_id */ typedef ulonglong query_id_t; -extern query_id_t query_id; +extern query_id_t global_query_id; /* increment query_id and return it. */ -inline query_id_t next_query_id() { return query_id++; } +inline query_id_t next_query_id() { return global_query_id++; } /* useful constants */ extern const key_map key_map_empty; @@ -87,26 +88,44 @@ void sql_kill(THD *thd, ulong id, bool only_kill_query); bool net_request_file(NET* net, const char* fname); char* query_table_status(THD *thd,const char *db,const char *table_name); +void net_set_write_timeout(NET *net, uint timeout); +void net_set_read_timeout(NET *net, uint timeout); + #define x_free(A) { my_free((gptr) (A),MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR)); } #define safeFree(x) { if(x) { my_free((gptr) x,MYF(0)); x = NULL; } } #define PREV_BITS(type,A) ((type) (((type) 1 << (A)) -1)) #define all_bits_set(A,B) ((A) & (B) != (B)) -#define WARN_DEPRECATED(Thd,Ver,Old,New) \ - do { \ - DBUG_ASSERT(strncmp(Ver, MYSQL_SERVER_VERSION, sizeof(Ver)-1) >= 0); \ - push_warning_printf(((THD *)Thd), MYSQL_ERROR::WARN_LEVEL_WARN, \ - ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), \ - (Old), (Ver), (New)); \ +#define WARN_DEPRECATED(Thd,Ver,Old,New) \ + do { \ + DBUG_ASSERT(strncmp(Ver, MYSQL_SERVER_VERSION, sizeof(Ver)-1) > 0); \ + if (((gptr)Thd) != NULL) \ + push_warning_printf(((THD *)Thd), MYSQL_ERROR::WARN_LEVEL_WARN, \ + ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), \ + (Old), (Ver), (New)); \ + else \ + sql_print_warning("The syntax %s is deprecated and will be removed " \ + "in MySQL %s. Please use %s instead.", (Old), (Ver), (New)); \ } while(0) - extern CHARSET_INFO *system_charset_info, *files_charset_info ; extern CHARSET_INFO *national_charset_info, *table_alias_charset; +enum Derivation +{ + DERIVATION_IGNORABLE= 5, + DERIVATION_COERCIBLE= 4, + DERIVATION_SYSCONST= 3, + DERIVATION_IMPLICIT= 2, + DERIVATION_NONE= 1, + DERIVATION_EXPLICIT= 0 +}; + + typedef struct my_locale_st { + uint number; const char *name; const char *description; const bool is_ascii; @@ -115,9 +134,11 @@ typedef struct my_locale_st TYPELIB *day_names; TYPELIB *ab_day_names; #ifdef __cplusplus - my_locale_st(const char *name_par, const char *descr_par, bool is_ascii_par, + my_locale_st(uint number_par, + const char *name_par, const char *descr_par, bool is_ascii_par, TYPELIB *month_names_par, TYPELIB *ab_month_names_par, TYPELIB *day_names_par, TYPELIB *ab_day_names_par) : + number(number_par), name(name_par), description(descr_par), is_ascii(is_ascii_par), month_names(month_names_par), ab_month_names(ab_month_names_par), day_names(day_names_par), ab_day_names(ab_day_names_par) @@ -129,6 +150,7 @@ extern MY_LOCALE my_locale_en_US; extern MY_LOCALE *my_locales[]; MY_LOCALE *my_locale_by_name(const char *name); +MY_LOCALE *my_locale_by_number(uint number); /*************************************************************************** Configuration parameters @@ -140,6 +162,8 @@ MY_LOCALE *my_locale_by_name(const char *name); #define MAX_ACCEPT_RETRY 10 // Test accept this many times #define MAX_FIELDS_BEFORE_HASH 32 #define USER_VARS_HASH_SIZE 16 +#define TABLE_OPEN_CACHE_MIN 64 +#define TABLE_OPEN_CACHE_DEFAULT 64 /* Value of 9236 discovered through binary search 2006-09-26 on Ubuntu Dapper @@ -150,7 +174,7 @@ MY_LOCALE *my_locale_by_name(const char *name); Feel free to raise this by the smallest amount you can to get the "execution_constants" test to pass. */ -#define STACK_MIN_SIZE 10788 // Abort if less stack during eval. +#define STACK_MIN_SIZE 12000 // Abort if less stack during eval. #define STACK_MIN_SIZE_FOR_OPEN 1024*80 #define STACK_BUFF_ALLOC 256 // For stack overrun checks @@ -221,12 +245,6 @@ MY_LOCALE *my_locale_by_name(const char *name); /* Characters shown for the command in 'information_schema.processlist' */ #define PROCESS_LIST_INFO_WIDTH 65535 -/* Time handling defaults */ -#define TIMESTAMP_MAX_YEAR 2038 -#define YY_PART_YEAR 70 -#define TIMESTAMP_MIN_YEAR (1900 + YY_PART_YEAR - 1) -#define TIMESTAMP_MAX_VALUE 2145916799 -#define TIMESTAMP_MIN_VALUE 1 #define PRECISION_FOR_DOUBLE 53 #define PRECISION_FOR_FLOAT 24 @@ -268,7 +286,6 @@ MY_LOCALE *my_locale_by_name(const char *name); #define TEST_MIT_THREAD 4 #define TEST_BLOCKING 8 #define TEST_KEEP_TMP_TABLES 16 -#define TEST_NO_THREADS 32 /* For debugging under Linux */ #define TEST_READCHECK 64 /* Force use of readcheck */ #define TEST_NO_EXTRA 128 #define TEST_CORE_ON_SIGNAL 256 /* Give core if signal */ @@ -296,54 +313,55 @@ MY_LOCALE *my_locale_by_name(const char *name); TODO: separate three contexts above, move them to separate bitfields. */ -#define SELECT_DISTINCT (LL(1) << 0) // SELECT, user -#define SELECT_STRAIGHT_JOIN (LL(1) << 1) // SELECT, user -#define SELECT_DESCRIBE (LL(1) << 2) // SELECT, user -#define SELECT_SMALL_RESULT (LL(1) << 3) // SELECT, user -#define SELECT_BIG_RESULT (LL(1) << 4) // SELECT, user -#define OPTION_FOUND_ROWS (LL(1) << 5) // SELECT, user -#define OPTION_TO_QUERY_CACHE (LL(1) << 6) // SELECT, user -#define SELECT_NO_JOIN_CACHE (LL(1) << 7) // intern -#define OPTION_BIG_TABLES (LL(1) << 8) // THD, user -#define OPTION_BIG_SELECTS (LL(1) << 9) // THD, user -#define OPTION_LOG_OFF (LL(1) << 10) // THD, user -#define OPTION_QUOTE_SHOW_CREATE (LL(1) << 11) // THD, user -#define TMP_TABLE_ALL_COLUMNS (LL(1) << 12) // SELECT, intern -#define OPTION_WARNINGS (LL(1) << 13) // THD, user -#define OPTION_AUTO_IS_NULL (LL(1) << 14) // THD, user, binlog -#define OPTION_FOUND_COMMENT (LL(1) << 15) // SELECT, intern, parser -#define OPTION_SAFE_UPDATES (LL(1) << 16) // THD, user -#define OPTION_BUFFER_RESULT (LL(1) << 17) // SELECT, user -#define OPTION_BIN_LOG (LL(1) << 18) // THD, user -#define OPTION_NOT_AUTOCOMMIT (LL(1) << 19) // THD, user -#define OPTION_BEGIN (LL(1) << 20) // THD, intern -#define OPTION_TABLE_LOCK (LL(1) << 21) // THD, intern -#define OPTION_QUICK (LL(1) << 22) // SELECT (for DELETE) -#define OPTION_KEEP_LOG (LL(1) << 23) // Keep binlog on rollback +#define SELECT_DISTINCT (ULL(1) << 0) // SELECT, user +#define SELECT_STRAIGHT_JOIN (ULL(1) << 1) // SELECT, user +#define SELECT_DESCRIBE (ULL(1) << 2) // SELECT, user +#define SELECT_SMALL_RESULT (ULL(1) << 3) // SELECT, user +#define SELECT_BIG_RESULT (ULL(1) << 4) // SELECT, user +#define OPTION_FOUND_ROWS (ULL(1) << 5) // SELECT, user +#define OPTION_TO_QUERY_CACHE (ULL(1) << 6) // SELECT, user +#define SELECT_NO_JOIN_CACHE (ULL(1) << 7) // intern +#define OPTION_BIG_TABLES (ULL(1) << 8) // THD, user +#define OPTION_BIG_SELECTS (ULL(1) << 9) // THD, user +#define OPTION_LOG_OFF (ULL(1) << 10) // THD, user +#define OPTION_QUOTE_SHOW_CREATE (ULL(1) << 11) // THD, user, unused +#define TMP_TABLE_ALL_COLUMNS (ULL(1) << 12) // SELECT, intern +#define OPTION_WARNINGS (ULL(1) << 13) // THD, user +#define OPTION_AUTO_IS_NULL (ULL(1) << 14) // THD, user, binlog +#define OPTION_FOUND_COMMENT (ULL(1) << 15) // SELECT, intern, parser +#define OPTION_SAFE_UPDATES (ULL(1) << 16) // THD, user +#define OPTION_BUFFER_RESULT (ULL(1) << 17) // SELECT, user +#define OPTION_BIN_LOG (ULL(1) << 18) // THD, user +#define OPTION_NOT_AUTOCOMMIT (ULL(1) << 19) // THD, user +#define OPTION_BEGIN (ULL(1) << 20) // THD, intern +#define OPTION_TABLE_LOCK (ULL(1) << 21) // THD, intern +#define OPTION_QUICK (ULL(1) << 22) // SELECT (for DELETE) +#define OPTION_KEEP_LOG (ULL(1) << 23) // THD, user /* The following is used to detect a conflict with DISTINCT */ -#define SELECT_ALL (LL(1) << 24) // SELECT, user, parser +#define SELECT_ALL (ULL(1) << 24) // SELECT, user, parser /* Set if we are updating a non-transaction safe table */ -#define OPTION_STATUS_NO_TRANS_UPDATE (LL(1) << 25) // THD, intern +#define OPTION_STATUS_NO_TRANS_UPDATE (ULL(1) << 25) // THD, intern /* The following can be set when importing tables in a 'wrong order' to suppress foreign key checks */ -#define OPTION_NO_FOREIGN_KEY_CHECKS (LL(1) << 26) // THD, user, binlog +#define OPTION_NO_FOREIGN_KEY_CHECKS (ULL(1) << 26) // THD, user, binlog /* The following speeds up inserts to InnoDB tables by suppressing unique key checks in some cases */ -#define OPTION_RELAXED_UNIQUE_CHECKS (LL(1) << 27) // THD, user, binlog -#define SELECT_NO_UNLOCK (LL(1) << 28) // SELECT, intern -#define OPTION_SCHEMA_TABLE (LL(1) << 29) // SELECT, intern +#define OPTION_RELAXED_UNIQUE_CHECKS (ULL(1) << 27) // THD, user, binlog +#define SELECT_NO_UNLOCK (ULL(1) << 28) // SELECT, intern +#define OPTION_SCHEMA_TABLE (ULL(1) << 29) // SELECT, intern /* Flag set if setup_tables already done */ -#define OPTION_SETUP_TABLES_DONE (LL(1) << 30) // intern +#define OPTION_SETUP_TABLES_DONE (ULL(1) << 30) // intern /* If not set then the thread will ignore all warnings with level notes. */ -#define OPTION_SQL_NOTES (LL(1) << 31) // THD, user -/* +#define OPTION_SQL_NOTES (ULL(1) << 31) // THD, user +/* Force the used temporary table to be a MyISAM table (because we will use fulltext functions when reading from it. */ -#define TMP_TABLE_FORCE_MYISAM (LL(1) << 32) +#define TMP_TABLE_FORCE_MYISAM (ULL(1) << 32) + /* Maximum length of time zone name that we support @@ -409,7 +427,11 @@ MY_LOCALE *my_locale_by_name(const char *name); #define UNCACHEABLE_EXPLAIN 8 /* Don't evaluate subqueries in prepare even if they're not correlated */ #define UNCACHEABLE_PREPARE 16 +/* For uncorrelated SELECT in an UNION with some correlated SELECTs */ +#define UNCACHEABLE_UNITED 32 +/* Used to check GROUP BY list in the MODE_ONLY_FULL_GROUP_BY mode */ +#define UNDEF_POS (-1) #ifdef EXTRA_DEBUG /* Sync points allow us to force the server to reach a certain line of code @@ -617,6 +639,7 @@ Item *negate_expression(THD *thd, Item *expr); #include "sql_acl.h" #include "tztime.h" #ifdef MYSQL_SERVER +#include "sql_servers.h" #include "opt_range.h" #ifdef HAVE_QUERY_CACHE @@ -767,6 +790,23 @@ uint build_table_path(char *buff, size_t bufflen, const char *db, void write_bin_log(THD *thd, bool clear_error, char const *query, ulong query_length); +/* sql_connect.cc */ +int check_user(THD *thd, enum enum_server_command command, + const char *passwd, uint passwd_len, const char *db, + bool check_count); +pthread_handler_t handle_one_connection(void *arg); +bool init_new_connection_handler_thread(); +void reset_mqh(LEX_USER *lu, bool get_them); +bool check_mqh(THD *thd, uint check_command); +void time_out_user_resource_limits(THD *thd, USER_CONN *uc); +int check_for_max_user_connections(THD *thd, USER_CONN *uc); +void decrease_user_connections(USER_CONN *uc); +void thd_init_client_charset(THD *thd, uint cs_number); +bool setup_connection_thread_globals(THD *thd); +bool login_connection(THD *thd); +void prepare_new_connection_state(THD* thd); +void end_connection(THD *thd); + bool mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create, bool silent); bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create); bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent); @@ -802,16 +842,14 @@ bool multi_delete_set_locks_and_link_aux_tables(LEX *lex); void init_max_user_conn(void); void init_update_queries(void); void free_max_user_conn(void); -pthread_handler_t handle_one_connection(void *arg); pthread_handler_t handle_bootstrap(void *arg); -void end_thread(THD *thd,bool put_in_cache); -void flush_thread_cache(); bool mysql_execute_command(THD *thd); bool do_command(THD *thd); bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length); void log_slow_statement(THD *thd); bool check_dup(const char *db, const char *name, TABLE_LIST *tables); +bool compare_record(TABLE *table); bool append_file_to_dir(THD *thd, const char **filename_ptr, const char *table_name); @@ -880,13 +918,15 @@ int setup_order(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, int setup_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, List<Item> &fields, List<Item> &all_fields, ORDER *order, bool *hidden_group_fields); +bool fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select, + Item **ref_pointer_array); bool handle_select(THD *thd, LEX *lex, select_result *result, ulong setup_tables_done_option); bool mysql_select(THD *thd, Item ***rref_pointer_array, TABLE_LIST *tables, uint wild_num, List<Item> &list, COND *conds, uint og_num, ORDER *order, ORDER *group, - Item *having, ORDER *proc_param, ulong select_type, + Item *having, ORDER *proc_param, ulonglong select_type, select_result *result, SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex); void free_underlaid_joins(THD *thd, SELECT_LEX *select); @@ -912,7 +952,7 @@ void sp_prepare_create_field(THD *thd, create_field *sql_field); int prepare_create_field(create_field *sql_field, uint *blob_columns, int *timestamps, int *timestamps_with_niladic, - uint table_flags); + longlong table_flags); bool mysql_create_table(THD *thd,const char *db, const char *table_name, HA_CREATE_INFO *create_info, List<create_field> &fields, List<Key> &keys, @@ -1081,7 +1121,8 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond); int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond); int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond); int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond); -bool get_schema_tables_result(JOIN *join); +bool get_schema_tables_result(JOIN *join, + enum enum_schema_table_state executed_place); #define is_schema_db(X) \ !my_strcasecmp(system_charset_info, information_schema_name.str, (X)) @@ -1105,6 +1146,7 @@ bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *, List<Item> *,enum ha_rkey_function,Item *,ha_rows,ha_rows); int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags, bool is_locked); +void mysql_ha_mark_tables_for_reopen(THD *thd, TABLE *table); /* mysql_ha_flush mode_flags bits */ #define MYSQL_HA_CLOSE_FINAL 0x00 #define MYSQL_HA_REOPEN_ON_USAGE 0x01 @@ -1134,7 +1176,8 @@ bool push_new_name_resolution_context(THD *thd, TABLE_LIST *left_op, TABLE_LIST *right_op); void add_join_on(TABLE_LIST *b,Item *expr); -void add_join_natural(TABLE_LIST *a,TABLE_LIST *b,List<String> *using_fields); +void add_join_natural(TABLE_LIST *a,TABLE_LIST *b,List<String> *using_fields, + SELECT_LEX *lex); bool add_proc_to_list(THD *thd, Item *item); TABLE *unlink_open_table(THD *thd,TABLE *list,TABLE *find); void update_non_unique_table_error(TABLE_LIST *update, @@ -1145,9 +1188,29 @@ SQL_SELECT *make_select(TABLE *head, table_map const_tables, table_map read_tables, COND *conds, bool allow_null_cond, int *error); extern Item **not_found_item; + +/* + This enumeration type is used only by the function find_item_in_list + to return the info on how an item has been resolved against a list + of possibly aliased items. + The item can be resolved: + - against an alias name of the list's element (RESOLVED_AGAINST_ALIAS) + - against non-aliased field name of the list (RESOLVED_WITH_NO_ALIAS) + - against an aliased field name of the list (RESOLVED_BEHIND_ALIAS) + - ignoring the alias name in cases when SQL requires to ignore aliases + (e.g. when the resolved field reference contains a table name or + when the resolved item is an expression) (RESOLVED_IGNORING_ALIAS) +*/ +enum enum_resolution_type { + NOT_RESOLVED=0, + RESOLVED_IGNORING_ALIAS, + RESOLVED_BEHIND_ALIAS, + RESOLVED_WITH_NO_ALIAS, + RESOLVED_AGAINST_ALIAS +}; Item ** find_item_in_list(Item *item, List<Item> &items, uint *counter, find_item_error_report_type report_error, - bool *unaliased); + enum_resolution_type *resolution); bool get_key_map_from_key_list(key_map *map, TABLE *table, List<String> *index_list); bool insert_fields(THD *thd, Name_resolution_context *context, @@ -1205,7 +1268,8 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, st_table_list *TABLE_LIST::*link, const char *db_name, const char *table_name); -TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list); +TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, + bool check_alias); TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name); TABLE *find_temporary_table(THD *thd, TABLE_LIST *table_list); bool close_temporary_table(THD *thd, TABLE_LIST *table_list); @@ -1427,7 +1491,8 @@ void print_plan(JOIN* join,uint idx, double record_count, double read_time, #endif void mysql_print_status(); /* key.cc */ -int find_ref_key(KEY *key, uint key_count, Field *field, uint *key_length); +int find_ref_key(KEY *key, uint key_count, byte *record, Field *field, + uint *key_length); void key_copy(byte *to_key, byte *from_record, KEY *key_info, uint key_length); void key_restore(byte *to_record, byte *from_key, KEY *key_info, uint key_length); @@ -1451,10 +1516,6 @@ typedef void (*sql_print_message_func)(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); extern sql_print_message_func sql_print_message_handlers[]; -/* type of the log table */ -#define QUERY_LOG_SLOW 1 -#define QUERY_LOG_GENERAL 2 - int error_log_print(enum loglevel level, const char *format, va_list args); @@ -1473,6 +1534,12 @@ File open_binlog(IO_CACHE *log, const char *log_file_name, /* mysqld.cc */ extern void MYSQLerror(const char*); void refresh_status(THD *thd); +my_bool mysql_rm_tmp_tables(void); +void handle_connection_in_main_thread(THD *thd); +void create_thread_to_handle_connection(THD *thd); +void unlink_thd(THD *thd); +bool one_thread_per_connection_end(THD *thd, bool put_in_cache); +void flush_thread_cache(); /* item_func.cc */ extern bool check_reserved_words(LEX_STRING *name); @@ -1485,6 +1552,8 @@ uint find_type2(TYPELIB *lib, const char *find, uint length, CHARSET_INFO *cs); void unhex_type2(TYPELIB *lib); uint check_word(TYPELIB *lib, const char *val, const char *end, const char **end_of_word); +int find_string_in_array(LEX_STRING * const haystack, LEX_STRING * const needle, + CHARSET_INFO * const cs); bool is_keyword(const char *name, uint len); @@ -1505,7 +1574,7 @@ extern int creating_table; // How many mysql_create_table() are running External variables */ -extern time_t start_time; +extern time_t server_start_time; extern char *mysql_data_home,server_version[SERVER_VERSION_LENGTH], mysql_real_data_home[], *opt_mysql_tmpdir, mysql_charsets_dir[], def_ft_boolean_syntax[sizeof(ft_boolean_syntax)]; @@ -1515,7 +1584,7 @@ extern const LEX_STRING command_name[]; extern const char *first_keyword, *my_localhost, *delayed_user, *binary_keyword; extern const char **errmesg; /* Error messages */ extern const char *myisam_recover_options_str; -extern const char *in_left_expr_name, *in_additional_cond; +extern const char *in_left_expr_name, *in_additional_cond, *in_having_cond; extern const char * const triggers_file_ext; extern const char * const trigname_file_ext; extern Eq_creator eq_creator; @@ -1530,10 +1599,11 @@ extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN]; extern char pidfile_name[FN_REFLEN], system_time_zone[30], *opt_init_file; extern char log_error_file[FN_REFLEN], *opt_tc_log_file; extern double log_10[32]; +extern double log_01[32]; extern ulonglong log_10_int[20]; extern ulonglong keybuff_size; extern ulonglong thd_startup_options; -extern ulong refresh_version,flush_version, thread_id; +extern ulong refresh_version, thread_id; extern ulong binlog_cache_use, binlog_cache_disk_use; extern ulong aborted_threads,aborted_connects; extern ulong delayed_insert_timeout; @@ -1552,10 +1622,8 @@ extern ulong query_buff_size, thread_stack; extern ulong max_prepared_stmt_count, prepared_stmt_count; extern ulong binlog_cache_size, max_binlog_cache_size, open_files_limit; extern ulong max_binlog_size, max_relay_log_size; -#ifdef HAVE_ROW_BASED_REPLICATION extern ulong opt_binlog_rows_event_max_size; -#endif -extern ulong rpl_recovery_rank, thread_cache_size; +extern ulong rpl_recovery_rank, thread_cache_size, thread_pool_size; extern ulong back_log; extern ulong specialflag, current_pid; extern ulong expire_logs_days, sync_binlog_period, sync_binlog_counter; @@ -1583,6 +1651,7 @@ extern my_bool opt_slave_compressed_protocol, use_temp_pool; extern my_bool opt_readonly, lower_case_file_system; extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs; extern my_bool opt_secure_auth; +extern char* opt_secure_file_priv; extern my_bool opt_log_slow_admin_statements; extern my_bool sp_automatic_privileges, opt_noacl; extern my_bool opt_old_style_user_limits, trust_function_creators; @@ -1640,27 +1709,24 @@ extern TABLE *unused_tables; extern const char* any_db; extern struct my_option my_long_options[]; extern const LEX_STRING view_type; +extern scheduler_functions thread_scheduler; +extern TYPELIB thread_handling_typelib; +extern uint8 uc_update_queries[SQLCOM_END+1]; extern uint sql_command_flags[]; extern TYPELIB log_output_typelib; /* optional things, have_* variables */ extern SHOW_COMP_OPTION have_innodb; -extern SHOW_COMP_OPTION have_example_db; -extern SHOW_COMP_OPTION have_archive_db; extern SHOW_COMP_OPTION have_csv_db; -extern SHOW_COMP_OPTION have_federated_db; -extern SHOW_COMP_OPTION have_blackhole_db; extern SHOW_COMP_OPTION have_ndbcluster; extern SHOW_COMP_OPTION have_partition_db; -extern SHOW_COMP_OPTION have_merge_db; extern handlerton *partition_hton; extern handlerton *myisam_hton; extern handlerton *heap_hton; -extern SHOW_COMP_OPTION have_row_based_replication; -extern SHOW_COMP_OPTION have_openssl, have_symlink, have_dlopen; +extern SHOW_COMP_OPTION have_ssl, have_symlink, have_dlopen; extern SHOW_COMP_OPTION have_query_cache; extern SHOW_COMP_OPTION have_geometry, have_rtree_keys; extern SHOW_COMP_OPTION have_crypt; @@ -1716,7 +1782,7 @@ void unlock_table_names(THD *thd, TABLE_LIST *table_list, /* old unireg functions */ void unireg_init(ulong options); -void unireg_end(void); +void unireg_end(void) __attribute__((noreturn)); bool mysql_create_frm(THD *thd, const char *file_name, const char *db, const char *table, HA_CREATE_INFO *create_info, @@ -1798,7 +1864,7 @@ ha_rows filesort(THD *thd, TABLE *form,struct st_sort_field *sortorder, uint s_length, SQL_SELECT *select, ha_rows max_rows, bool sort_positions, ha_rows *examined_rows); -void filesort_free_buffers(TABLE *table); +void filesort_free_buffers(TABLE *table, bool full); void change_double_for_sort(double nr,byte *to); double my_double_round(double value, int dec, bool truncate); int get_quick_record(SQL_SELECT *select); @@ -1818,7 +1884,7 @@ int create_frm(THD *thd, const char *name, const char *db, const char *table, HA_CREATE_INFO *create_info, uint keys); void update_create_info_from_table(HA_CREATE_INFO *info, TABLE *form); int rename_file_ext(const char * from,const char * to,const char * ext); -bool check_db_name(char *db); +bool check_db_name(LEX_STRING *db); bool check_column_name(const char *name); bool check_table_name(const char *name, uint length); char *get_field(MEM_ROOT *mem, Field *field); @@ -1837,6 +1903,7 @@ uint build_table_filename(char *buff, size_t bufflen, const char *db, #define FN_FROM_IS_TMP (1 << 0) #define FN_TO_IS_TMP (1 << 1) #define FN_IS_TMP (FN_FROM_IS_TMP | FN_TO_IS_TMP) +#define NO_FRM_RENAME (1 << 2) /* from hostname.cc */ struct in_addr; @@ -2001,7 +2068,7 @@ inline bool is_user_table(TABLE * table) */ #ifndef EMBEDDED_LIBRARY -extern "C" void unireg_abort(int exit_code); +extern "C" void unireg_abort(int exit_code) __attribute__((noreturn)); void kill_delayed_threads(void); bool check_stack_overrun(THD *thd, long margin, char *dummy); #else @@ -2011,8 +2078,51 @@ inline void kill_delayed_threads(void) {} #endif /* Used by handlers to store things in schema tables */ +#define IS_FILES_FILE_ID 0 +#define IS_FILES_FILE_NAME 1 +#define IS_FILES_FILE_TYPE 2 +#define IS_FILES_TABLESPACE_NAME 3 +#define IS_FILES_TABLE_CATALOG 4 +#define IS_FILES_TABLE_SCHEMA 5 +#define IS_FILES_TABLE_NAME 6 +#define IS_FILES_LOGFILE_GROUP_NAME 7 +#define IS_FILES_LOGFILE_GROUP_NUMBER 8 +#define IS_FILES_ENGINE 9 +#define IS_FILES_FULLTEXT_KEYS 10 +#define IS_FILES_DELETED_ROWS 11 +#define IS_FILES_UPDATE_COUNT 12 +#define IS_FILES_FREE_EXTENTS 13 +#define IS_FILES_TOTAL_EXTENTS 14 +#define IS_FILES_EXTENT_SIZE 15 +#define IS_FILES_INITIAL_SIZE 16 +#define IS_FILES_MAXIMUM_SIZE 17 +#define IS_FILES_AUTOEXTEND_SIZE 18 +#define IS_FILES_CREATION_TIME 19 +#define IS_FILES_LAST_UPDATE_TIME 20 +#define IS_FILES_LAST_ACCESS_TIME 21 +#define IS_FILES_RECOVER_TIME 22 +#define IS_FILES_TRANSACTION_COUNTER 23 +#define IS_FILES_VERSION 24 +#define IS_FILES_ROW_FORMAT 25 +#define IS_FILES_TABLE_ROWS 26 +#define IS_FILES_AVG_ROW_LENGTH 27 +#define IS_FILES_DATA_LENGTH 28 +#define IS_FILES_MAX_DATA_LENGTH 29 +#define IS_FILES_INDEX_LENGTH 30 +#define IS_FILES_DATA_FREE 31 +#define IS_FILES_CREATE_TIME 32 +#define IS_FILES_UPDATE_TIME 33 +#define IS_FILES_CHECK_TIME 34 +#define IS_FILES_CHECKSUM 35 +#define IS_FILES_STATUS 36 +#define IS_FILES_EXTRA 37 +void init_fill_schema_files_row(TABLE* table); bool schema_table_store_record(THD *thd, TABLE *table); +/* sql/item_create.cc */ +int item_create_init(); +void item_create_cleanup(); + #endif /* MYSQL_SERVER */ #endif /* MYSQL_CLIENT */ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 6674dd87757..758dc54316e 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -28,9 +27,7 @@ #include "../storage/myisam/ha_myisam.h" -#ifdef HAVE_ROW_BASED_REPLICATION #include "rpl_injector.h" -#endif #ifdef WITH_INNOBASE_STORAGE_ENGINE #define OPT_INNODB_DEFAULT 1 @@ -50,6 +47,10 @@ #define OPT_NDBCLUSTER_DEFAULT 0 #endif +#ifndef DEFAULT_SKIP_THREAD_PRIORITY +#define DEFAULT_SKIP_THREAD_PRIORITY 0 +#endif + #include <thr_alarm.h> #include <ft_global.h> #include <errmsg.h> @@ -58,16 +59,18 @@ #define mysqld_charset &my_charset_latin1 -#ifndef DBUG_OFF -#define ONE_THREAD -#endif - #ifdef HAVE_purify #define IF_PURIFY(A,B) (A) #else #define IF_PURIFY(A,B) (B) #endif +#if SIZEOF_CHARP == 4 +#define MAX_MEM_TABLE_SIZE ~(ulong) 0 +#else +#define MAX_MEM_TABLE_SIZE ~(ulonglong) 0 +#endif + /* stack traces are only supported on linux intel */ #if defined(__linux__) && defined(__i386__) && defined(USE_PSTACK) #define HAVE_STACK_TRACE_ON_SEGV @@ -275,6 +278,16 @@ static TYPELIB tc_heuristic_recover_typelib= array_elements(tc_heuristic_recover_names)-1,"", tc_heuristic_recover_names, NULL }; + +static const char *thread_handling_names[]= +{ "one-thread-per-connection", "no-threads", "pool-of-threads", NullS}; + +TYPELIB thread_handling_typelib= +{ + array_elements(thread_handling_names) - 1, "", + thread_handling_names, NULL +}; + const char *first_keyword= "first", *binary_keyword= "BINARY"; const char *my_localhost= "localhost", *delayed_user= "DELAYED"; #if SIZEOF_OFF_T > 4 && defined(BIG_TABLES) @@ -310,7 +323,7 @@ static bool lower_case_table_names_used= 0; static bool volatile select_thread_in_use, signal_thread_in_use; static bool volatile ready_to_exit; static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0; -static my_bool opt_isam, opt_ndbcluster, opt_merge; +static my_bool opt_ndbcluster; static my_bool opt_short_log_format= 0; static uint kill_cached_threads, wake_thread; static ulong killed_threads, thread_created; @@ -340,7 +353,7 @@ bool opt_error_log= IF_WIN(1,0); bool opt_disable_networking=0, opt_skip_show_db=0; my_bool opt_character_set_client_handshake= 1; bool server_id_supplied = 0; -bool opt_endinfo,using_udf_functions; +bool opt_endinfo, using_udf_functions; my_bool locked_in_memory; bool opt_using_transactions, using_update_log; bool volatile abort_loop; @@ -354,6 +367,7 @@ my_bool opt_safe_user_create = 0, opt_no_mix_types = 0; my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0; my_bool opt_log_slave_updates= 0; my_bool opt_innodb; +bool slave_warning_issued = false; /* Legacy global handlerton. These will be removed (please do not add more). @@ -372,7 +386,6 @@ extern longlong innobase_log_file_size; extern long innobase_log_buffer_size; extern longlong innobase_buffer_pool_size; extern long innobase_additional_mem_pool_size; -extern long innobase_buffer_pool_awe_mem_mb; extern long innobase_file_io_threads, innobase_lock_wait_timeout; extern long innobase_force_recovery; extern long innobase_open_files; @@ -386,6 +399,8 @@ extern my_bool innobase_log_archive, innobase_use_large_pages, innobase_use_native_aio, innobase_file_per_table, innobase_locks_unsafe_for_binlog, + innobase_rollback_on_timeout, + innobase_stats_on_metadata, innobase_create_status_file; extern "C" { extern ulong srv_max_buf_pool_modified_pct; @@ -423,6 +438,7 @@ extern enum ndb_distribution opt_ndb_distribution_id; my_bool opt_readonly, use_temp_pool, relay_log_purge; my_bool opt_sync_frm, opt_allow_suspicious_udfs; my_bool opt_secure_auth= 0; +char* opt_secure_file_priv= 0; my_bool opt_log_slow_admin_statements= 0; my_bool lower_case_file_system= 0; my_bool opt_large_pages= 0; @@ -438,16 +454,13 @@ volatile bool mqh_used = 0; my_bool opt_noacl; my_bool sp_automatic_privileges= 1; -#ifdef HAVE_ROW_BASED_REPLICATION ulong opt_binlog_rows_event_max_size; const char *binlog_format_names[]= {"STATEMENT", "ROW", "MIXED", NullS}; -#else -const char *binlog_format_names[]= {"STATEMENT", NullS}; -#endif TYPELIB binlog_format_typelib= - { array_elements(binlog_format_names)-1,"", + { array_elements(binlog_format_names) - 1, "", binlog_format_names, NULL }; - +ulong opt_binlog_format_id= (ulong) BINLOG_FORMAT_UNSPEC; +const char *opt_binlog_format= binlog_format_names[opt_binlog_format_id]; #ifdef HAVE_INITGROUPS static bool calling_initgroups= FALSE; /* Used in SIGSEGV handler. */ #endif @@ -464,10 +477,11 @@ ulong thread_stack, what_to_log; ulong query_buff_size, slow_launch_time, slave_open_temp_tables; ulong open_files_limit, max_binlog_size, max_relay_log_size; ulong slave_net_timeout, slave_trans_retries; -ulong thread_cache_size=0, binlog_cache_size=0, max_binlog_cache_size=0; +ulong thread_cache_size=0, thread_pool_size= 0; +ulong binlog_cache_size=0, max_binlog_cache_size=0; ulong query_cache_size=0; -ulong refresh_version, flush_version; /* Increments on each reload */ -query_id_t query_id; +ulong refresh_version; /* Increments on each reload */ +query_id_t global_query_id; ulong aborted_threads, aborted_connects; ulong delayed_insert_timeout, delayed_insert_limit, delayed_queue_size; ulong delayed_insert_threads, delayed_insert_writes, delayed_rows_in_use; @@ -499,7 +513,8 @@ ulong rpl_recovery_rank=0; const char *log_output_str= "TABLE"; double log_10[32]; /* 10 potences */ -time_t start_time; +double log_01[32]; +time_t server_start_time; char mysql_home[FN_REFLEN], pidfile_name[FN_REFLEN], system_time_zone[30]; char *default_tz_name; @@ -515,16 +530,18 @@ key_map key_map_full(0); // Will be initialized later const char *opt_date_time_formats[3]; char mysql_data_home_buff[2], *mysql_data_home=mysql_real_data_home; -struct passwd *user_info; char server_version[SERVER_VERSION_LENGTH]; char *mysqld_unix_port, *opt_mysql_tmpdir; const char **errmesg; /* Error messages */ const char *myisam_recover_options_str="OFF"; const char *myisam_stats_method_str="nulls_unequal"; + /* name of reference on left espression in rewritten IN subquery */ const char *in_left_expr_name= "<left expr>"; /* name of additional condition */ const char *in_additional_cond= "<IN COND>"; +const char *in_having_cond= "<IN HAVING>"; + my_decimal decimal_zero; /* classes for comparation parsing/processing */ Eq_creator eq_creator; @@ -534,7 +551,6 @@ Lt_creator lt_creator; Ge_creator ge_creator; Le_creator le_creator; - FILE *bootstrap_file; int bootstrap_error; FILE *stderror_file=0; @@ -555,8 +571,7 @@ CHARSET_INFO *system_charset_info, *files_charset_info ; CHARSET_INFO *national_charset_info, *table_alias_charset; CHARSET_INFO *character_set_filesystem; -SHOW_COMP_OPTION have_row_based_replication; -SHOW_COMP_OPTION have_openssl, have_symlink, have_dlopen, have_query_cache; +SHOW_COMP_OPTION have_ssl, have_symlink, have_dlopen, have_query_cache; SHOW_COMP_OPTION have_geometry, have_rtree_keys; SHOW_COMP_OPTION have_crypt, have_compress; @@ -618,9 +633,13 @@ static char **defaults_argv; static char *opt_bin_logname; static my_socket unix_sock,ip_sock; -static pthread_t select_thread; struct rand_struct sql_rand; // used by sql_class.cc:THD::THD() +#ifndef EMBEDDED_LIBRARY +struct passwd *user_info; +static pthread_t select_thread; +#endif + /* OS specific variables */ #ifdef __WIN__ @@ -673,6 +692,8 @@ my_bool opt_enable_shared_memory; HANDLE smem_event_connect_request= 0; #endif +scheduler_functions thread_scheduler; + #define SSL_VARS_NOT_STATIC #include "sslopt-vars.h" #ifdef HAVE_OPENSSL @@ -697,7 +718,6 @@ struct st_VioSSLFd *ssl_acceptor_fd; /* Function declarations */ -static void start_signal_handler(void); pthread_handler_t signal_hand(void *arg); static void mysql_init_variables(void); static void get_options(int argc,char **argv); @@ -708,7 +728,6 @@ static void fix_paths(void); pthread_handler_t handle_connections_sockets(void *arg); pthread_handler_t kill_server_thread(void *arg); static void bootstrap(FILE *file); -static void close_server_sock(); static bool read_init_file(char *file_name); #ifdef __NT__ pthread_handler_t handle_connections_namedpipes(void *arg); @@ -719,11 +738,17 @@ pthread_handler_t handle_connections_shared_memory(void *arg); pthread_handler_t handle_slave(void *arg); static ulong find_bit_type(const char *x, TYPELIB *bit_lib); static void clean_up(bool print_message); +static int test_if_case_insensitive(const char *dir_name); + +#ifndef EMBEDDED_LIBRARY +static void start_signal_handler(void); +static void close_server_sock(); static void clean_up_mutexes(void); static void wait_for_signal_thread_to_end(void); -static int test_if_case_insensitive(const char *dir_name); static void create_pid_file(); static void end_ssl(); +#endif + #ifndef EMBEDDED_LIBRARY /**************************************************************************** @@ -849,6 +874,7 @@ static void close_connections(void) continue; tmp->killed= THD::KILL_CONNECTION; + thread_scheduler.post_kill_notification(tmp); if (tmp->mysys_var) { tmp->mysys_var->abort=1; @@ -913,7 +939,6 @@ static void close_connections(void) DBUG_PRINT("quit",("close_connections thread")); DBUG_VOID_RETURN; } -#endif /*EMBEDDED_LIBRARY*/ static void close_server_sock() @@ -956,12 +981,14 @@ static void close_server_sock() #endif } +#endif /*EMBEDDED_LIBRARY*/ + void kill_mysql(void) { DBUG_ENTER("kill_mysql"); -#ifdef SIGNALS_DONT_BREAK_READ +#if defined(SIGNALS_DONT_BREAK_READ) && !defined(EMBEDDED_LIBRARY) abort_loop=1; // Break connection loops close_server_sock(); // Force accept to wake up #endif @@ -1039,7 +1066,7 @@ static void __cdecl kill_server(int sig_ptr) kill_in_progress=TRUE; abort_loop=1; // This should be set if (sig != 0) // 0 is not a valid signal number - my_sigset(sig,SIG_IGN); + my_sigset(sig, SIG_IGN); /* purify inspected */ if (sig == MYSQL_KILL_SIGNAL || sig == 0) sql_print_information(ER(ER_NORMAL_SHUTDOWN),my_progname); else @@ -1057,9 +1084,6 @@ static void __cdecl kill_server(int sig_ptr) } #endif -#if defined(__NETWARE__) || (defined(USE_ONE_SIGNAL_HAND) && !defined(__WIN__)) - my_thread_init(); // If this is a new thread -#endif close_connections(); if (sig != MYSQL_KILL_SIGNAL && #ifdef __WIN__ @@ -1070,16 +1094,15 @@ static void __cdecl kill_server(int sig_ptr) else unireg_end(); + /* purecov: begin deadcode */ #ifdef __NETWARE__ if (!event_flag) pthread_join(select_thread, NULL); // wait for main thread #endif /* __NETWARE__ */ -#if defined(__NETWARE__) || (defined(USE_ONE_SIGNAL_HAND) && !defined(__WIN__) && !defined(OS2)) my_thread_end(); -#endif - - pthread_exit(0); /* purecov: deadcode */ + pthread_exit(0); + /* purecov: end */ #endif /* EMBEDDED_LIBRARY */ RETURN_FROM_KILL_SERVER; @@ -1091,11 +1114,15 @@ pthread_handler_t kill_server_thread(void *arg __attribute__((unused))) { my_thread_init(); // Initialize new thread kill_server(0); - my_thread_end(); // Normally never reached + /* purecov: begin deadcode */ + my_thread_end(); + pthread_exit(0); return 0; + /* purecov: end */ } #endif + extern "C" sig_handler print_signal_warning(int sig) { if (global_system_variables.log_warnings) @@ -1163,9 +1190,7 @@ void clean_up(bool print_message) what they have that is dependent on the binlog */ ha_binlog_end(current_thd); -#ifdef HAVE_ROW_BASED_REPLICATION injector::free_instance(); -#endif mysql_bin_log.cleanup(); #ifdef HAVE_REPLICATION @@ -1175,6 +1200,7 @@ void clean_up(bool print_message) my_tz_free(); my_database_names_free(); #ifndef NO_EMBEDDED_ACCESS_CHECKS + servers_free(1); acl_free(1); grant_free(); #endif @@ -1184,9 +1210,9 @@ void clean_up(bool print_message) hostname_cache_free(); item_user_lock_free(); lex_free(); /* Free some memory */ + item_create_cleanup(); set_var_free(); free_charsets(); - (void) ha_panic(HA_PANIC_CLOSE); /* close all tables and logs */ if (!opt_noacl) { #ifdef HAVE_DLOPEN @@ -1194,6 +1220,7 @@ void clean_up(bool print_message) #endif } plugin_shutdown(); + ha_end(); if (tc_log) tc_log->close(); xid_cache_free(); @@ -1220,6 +1247,7 @@ void clean_up(bool print_message) #endif x_free(opt_bin_logname); x_free(opt_relay_logname); + x_free(opt_secure_file_priv); bitmap_free(&temp_pool); free_max_user_conn(); #ifdef HAVE_REPLICATION @@ -1227,7 +1255,9 @@ void clean_up(bool print_message) #endif delete binlog_filter; delete rpl_filter; +#ifndef EMBEDDED_LIBRARY end_ssl(); +#endif vio_end(); #ifdef USE_REGEX my_regex_end(); @@ -1239,6 +1269,7 @@ void clean_up(bool print_message) if (!opt_bootstrap) (void) my_delete(pidfile_name,MYF(0)); // This may not always exist #endif + thread_scheduler.end(); finish_client_errs(); my_free((gptr) my_error_unregister(ER_ERROR_FIRST, ER_ERROR_LAST), MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR)); @@ -1260,6 +1291,8 @@ void clean_up(bool print_message) } /* clean_up */ +#ifndef EMBEDDED_LIBRARY + /* This is mainly needed when running with purify, but it's still nice to know that all child threads have died when mysqld exits @@ -1330,6 +1363,9 @@ static void clean_up_mutexes() (void) pthread_cond_destroy(&COND_manager); } +#endif /*EMBEDDED_LIBRARY*/ + + /**************************************************************************** ** Init IP and UNIX socket ****************************************************************************/ @@ -1364,7 +1400,7 @@ static void set_ports() static struct passwd *check_user(const char *user) { #if !defined(__WIN__) && !defined(__NETWARE__) - struct passwd *user_info; + struct passwd *tmp_user_info; uid_t user_id= geteuid(); // Don't bother if we aren't superuser @@ -1372,12 +1408,14 @@ static struct passwd *check_user(const char *user) { if (user) { - // Don't give a warning, if real user is same as given with --user - user_info= getpwnam(user); - if ((!user_info || user_id != user_info->pw_uid) && + /* Don't give a warning, if real user is same as given with --user */ + /* purecov: begin tested */ + tmp_user_info= getpwnam(user); + if ((!tmp_user_info || user_id != tmp_user_info->pw_uid) && global_system_variables.log_warnings) sql_print_warning( "One can only use the --user switch if running as root\n"); + /* purecov: end */ } return NULL; } @@ -1390,23 +1428,22 @@ static struct passwd *check_user(const char *user) } return NULL; } + /* purecov: begin tested */ if (!strcmp(user,"root")) return NULL; // Avoid problem with dynamic libraries - if (!(user_info= getpwnam(user))) + if (!(tmp_user_info= getpwnam(user))) { // Allow a numeric uid to be used const char *pos; for (pos= user; my_isdigit(mysqld_charset,*pos); pos++) ; if (*pos) // Not numeric id goto err; - if (!(user_info= getpwuid(atoi(user)))) + if (!(tmp_user_info= getpwuid(atoi(user)))) goto err; - else - return user_info; } - else - return user_info; + return tmp_user_info; + /* purecov: end */ err: sql_print_error("Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n",user); @@ -1415,10 +1452,11 @@ err: return NULL; } -static void set_user(const char *user, struct passwd *user_info) +static void set_user(const char *user, struct passwd *user_info_arg) { + /* purecov: begin tested */ #if !defined(__WIN__) && !defined(__NETWARE__) - DBUG_ASSERT(user_info != 0); + DBUG_ASSERT(user_info_arg != 0); #ifdef HAVE_INITGROUPS /* We can get a SIGSEGV when calling initgroups() on some systems when NSS @@ -1427,33 +1465,34 @@ static void set_user(const char *user, struct passwd *user_info) output a specific message to help the user resolve this problem. */ calling_initgroups= TRUE; - initgroups((char*) user, user_info->pw_gid); + initgroups((char*) user, user_info_arg->pw_gid); calling_initgroups= FALSE; #endif - if (setgid(user_info->pw_gid) == -1) + if (setgid(user_info_arg->pw_gid) == -1) { sql_perror("setgid"); unireg_abort(1); } - if (setuid(user_info->pw_uid) == -1) + if (setuid(user_info_arg->pw_uid) == -1) { sql_perror("setuid"); unireg_abort(1); } #endif + /* purecov: end */ } -static void set_effective_user(struct passwd *user_info) +static void set_effective_user(struct passwd *user_info_arg) { #if !defined(__WIN__) && !defined(__NETWARE__) - DBUG_ASSERT(user_info != 0); - if (setregid((gid_t)-1, user_info->pw_gid) == -1) + DBUG_ASSERT(user_info_arg != 0); + if (setregid((gid_t)-1, user_info_arg->pw_gid) == -1) { sql_perror("setregid"); unireg_abort(1); } - if (setreuid((uid_t)-1, user_info->pw_uid) == -1) + if (setreuid((uid_t)-1, user_info_arg->pw_uid) == -1) { sql_perror("setreuid"); unireg_abort(1); @@ -1490,6 +1529,9 @@ static void network_init(void) DBUG_ENTER("network_init"); LINT_INIT(ret); + if (thread_scheduler.init()) + unireg_abort(1); /* purecov: inspected */ + set_ports(); if (mysqld_port != 0 && !opt_disable_networking && !opt_bootstrap) @@ -1609,8 +1651,8 @@ static void network_init(void) if (strlen(mysqld_unix_port) > (sizeof(UNIXaddr.sun_path) - 1)) { - sql_print_error("The socket file path is too long (> %lu): %s", - sizeof(UNIXaddr.sun_path) - 1, mysqld_unix_port); + sql_print_error("The socket file path is too long (> %u): %s", + (uint) sizeof(UNIXaddr.sun_path) - 1, mysqld_unix_port); unireg_abort(1); } if ((unix_sock= socket(AF_UNIX, SOCK_STREAM, 0)) < 0) @@ -1647,18 +1689,6 @@ static void network_init(void) #endif /*!EMBEDDED_LIBRARY*/ -void MYSQLerror(const char *s) -{ - THD *thd=current_thd; - char *yytext= (char*) thd->lex->tok_start; - /* "parse error" changed into "syntax error" between bison 1.75 and 1.875 */ - if (strcmp(s,"parse error") == 0 || strcmp(s,"syntax error") == 0) - s=ER(ER_SYNTAX_ERROR); - my_printf_error(ER_PARSE_ERROR, ER(ER_PARSE_ERROR), MYF(0), s, - (yytext ? (char*) yytext : ""), - thd->lex->yylineno); -} - #ifndef EMBEDDED_LIBRARY /* @@ -1708,21 +1738,55 @@ extern "C" sig_handler end_thread_signal(int sig __attribute__((unused))) if (thd && ! thd->bootstrap) { statistic_increment(killed_threads, &LOCK_status); - end_thread(thd,0); + thread_scheduler.end_thread(thd,0); /* purecov: inspected */ } DBUG_VOID_RETURN; /* purecov: deadcode */ } -void end_thread(THD *thd, bool put_in_cache) +/* + Unlink thd from global list of available connections and free thd + + SYNOPSIS + unlink_thd() + thd Thread handler + + NOTES + LOCK_thread_count is locked and left locked +*/ + +void unlink_thd(THD *thd) { - DBUG_ENTER("end_thread"); + DBUG_ENTER("unlink_thd"); + DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd)); thd->cleanup(); (void) pthread_mutex_lock(&LOCK_thread_count); thread_count--; delete thd; + DBUG_VOID_RETURN; +} + + +/* + Store thread in cache for reuse by new connections + + SYNOPSIS + cache_thread() + + NOTES + LOCK_thread_count has to be locked + + RETURN + 0 Thread was not put in cache + 1 Thread is to be reused by new connection. + (ie, caller should return, not abort with pthread_exit()) +*/ + - if (put_in_cache && cached_thread_count < thread_cache_size && +static bool cache_thread() +{ + safe_mutex_assert_owner(&LOCK_thread_count); + if (cached_thread_count < thread_cache_size && ! abort_loop && !kill_cached_threads) { /* Don't kill the thread, just put it in cache for reuse */ @@ -1735,31 +1799,56 @@ void end_thread(THD *thd, bool put_in_cache) pthread_cond_signal(&COND_flush_thread_cache); if (wake_thread) { + THD *thd; wake_thread--; - thd=thread_cache.get(); - thd->real_id=pthread_self(); + thd= thread_cache.get(); thd->thread_stack= (char*) &thd; // For store_globals (void) thd->store_globals(); thd->thr_create_time= time(NULL); threads.append(thd); - pthread_mutex_unlock(&LOCK_thread_count); - DBUG_VOID_RETURN; + return(1); } } + return(0); +} + + +/* + End thread for the current connection + + SYNOPSIS + one_thread_per_connection_end() + thd Thread handler + put_in_cache Store thread in cache, if there is room in it + Normally this is true in all cases except when we got + out of resources initializing the current thread + + NOTES + If thread is cached, we will wait until thread is scheduled to be + reused and then we will return. + If thread is not cached, we end the thread. + + RETURN + 0 Signal to handle_one_connection to reuse connection +*/ + +bool one_thread_per_connection_end(THD *thd, bool put_in_cache) +{ + DBUG_ENTER("one_thread_per_connection_end"); + unlink_thd(thd); + if (put_in_cache) + put_in_cache= cache_thread(); + pthread_mutex_unlock(&LOCK_thread_count); + if (put_in_cache) + DBUG_RETURN(0); // Thread is reused - /* Tell main we are ready */ - (void) pthread_mutex_unlock(&LOCK_thread_count); /* It's safe to broadcast outside a lock (COND... is not deleted here) */ DBUG_PRINT("signal", ("Broadcasting COND_thread_count")); (void) pthread_cond_broadcast(&COND_thread_count); -#ifdef ONE_THREAD - if (!(test_flags & TEST_NO_THREADS)) // For debugging under Linux -#endif - { - my_thread_end(); - pthread_exit(0); - } - DBUG_VOID_RETURN; + + my_thread_end(); + pthread_exit(0); + DBUG_RETURN(0); // Impossible } @@ -1794,6 +1883,7 @@ extern "C" sig_handler abort_thread(int sig __attribute__((unused))) } #endif + /****************************************************************************** Setup a signal thread with handles all signals. Because Linux doesn't support schemas use a mutex to check that @@ -1813,6 +1903,7 @@ static void init_signals(void) #endif } + static void start_signal_handler(void) { // Save vm id of this process @@ -1820,6 +1911,7 @@ static void start_signal_handler(void) create_pid_file(); } + static void check_data_home(const char *path) {} @@ -2040,6 +2132,7 @@ static void init_signals(void) } + static void start_signal_handler(void) { // Save vm id of this process @@ -2068,7 +2161,10 @@ static void check_data_home(const char *path) extern "C" sig_handler handle_segfault(int sig) { + time_t curr_time; + struct tm tm; THD *thd=current_thd; + /* Strictly speaking, one needs a mutex here but since we have got SIGSEGV already, things are a mess @@ -2082,11 +2178,17 @@ extern "C" sig_handler handle_segfault(int sig) } segfaulted = 1; + + curr_time= time(NULL); + localtime_r(&curr_time, &tm); + fprintf(stderr,"\ -mysqld got signal %d;\n\ +%02d%02d%02d %2d:%02d:%02d - mysqld got signal %d;\n\ This could be because you hit a bug. It is also possible that this binary\n\ or one of the libraries it was linked against is corrupt, improperly built,\n\ or misconfigured. This error can also be caused by malfunctioning hardware.\n", + tm.tm_year % 100, tm.tm_mon+1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, sig); fprintf(stderr, "\ We will try our best to scrape up some info that will hopefully help diagnose\n\ @@ -2096,14 +2198,15 @@ and this may fail.\n\n"); (ulong) dflt_key_cache->key_cache_mem_size); fprintf(stderr, "read_buffer_size=%ld\n", (long) global_system_variables.read_buff_size); fprintf(stderr, "max_used_connections=%lu\n", max_used_connections); - fprintf(stderr, "max_connections=%lu\n", max_connections); + fprintf(stderr, "max_threads=%u\n", thread_scheduler.max_threads); fprintf(stderr, "threads_connected=%u\n", thread_count); fprintf(stderr, "It is possible that mysqld could use up to \n\ -key_buffer_size + (read_buffer_size + sort_buffer_size)*max_connections = %lu K\n\ +key_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = %lu K\n\ bytes of memory\n", ((ulong) dflt_key_cache->key_cache_mem_size + (global_system_variables.read_buff_size + global_system_variables.sortbuff_size) * - max_connections)/ 1024); + thread_scheduler.max_threads + + max_connections * sizeof(THD)) / 1024); fprintf(stderr, "Hope that's ok; if not, decrease some variables in the equation.\n\n"); #if defined(HAVE_LINUXTHREADS) @@ -2121,7 +2224,7 @@ the thread stack. Please read http://www.mysql.com/doc/en/Linux.html\n\n", #ifdef HAVE_STACKTRACE if (!(test_flags & TEST_NO_STACKTRACE)) { - fprintf(stderr,"thd=%p\n",thd); + fprintf(stderr,"thd: 0x%lx\n",(long) thd); print_stacktrace(thd ? (gptr) thd->thread_stack : (gptr) 0, thread_stack); } @@ -2149,13 +2252,24 @@ later when used with nscd), disable LDAP in your nsswitch.conf, or use a\n\ mysqld that is not statically linked.\n"); #endif - if (test_flags & TEST_CORE_ON_SIGNAL) - { - fprintf(stderr, "Writing a core file\n"); - fflush(stderr); - write_core(sig); - } - exit(1); + if (locked_in_memory) + { + fprintf(stderr, "\n\ +The \"--memlock\" argument, which was enabled, uses system calls that are\n\ +unreliable and unstable on some operating systems and operating-system\n\ +versions (notably, some versions of Linux). This crash could be due to use\n\ +of those buggy OS calls. You should consider whether you really need the\n\ +\"--memlock\" parameter and/or consult the OS distributer about \"mlockall\"\n\ +bugs.\n"); + } + + if (test_flags & TEST_CORE_ON_SIGNAL) + { + fprintf(stderr, "Writing a core file\n"); + fflush(stderr); + write_core(sig); + } + exit(1); } #ifndef SA_RESETHAND @@ -2165,6 +2279,8 @@ mysqld that is not statically linked.\n"); #define SA_NODEFER 0 #endif +#ifndef EMBEDDED_LIBRARY + static void init_signals(void) { sigset_t set; @@ -2237,7 +2353,6 @@ static void init_signals(void) } -#ifndef EMBEDDED_LIBRARY static void start_signal_handler(void) { int error; @@ -2292,7 +2407,7 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused))) This should actually be '+ max_number_of_slaves' instead of +10, but the +10 should be quite safe. */ - init_thr_alarm(max_connections + + init_thr_alarm(thread_scheduler.max_threads + global_system_variables.max_insert_delayed_threads + 10); #if SIGINT != THR_KILL_SIGNAL if (test_flags & TEST_SIGINT) @@ -2411,11 +2526,11 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused))) } return(0); /* purecov: deadcode */ } -#endif /*!EMBEDDED_LIBRARY*/ static void check_data_home(const char *path) {} +#endif /*!EMBEDDED_LIBRARY*/ #endif /* __WIN__*/ @@ -2438,6 +2553,14 @@ static int my_message_sql(uint error, const char *str, myf MyFlags) */ if ((thd= current_thd)) { + /* + TODO: There are two exceptions mechanism (THD and sp_rcontext), + this could be improved by having a common stack of handlers. + */ + if (thd->handle_error(error, + MYSQL_ERROR::WARN_LEVEL_ERROR)) + DBUG_RETURN(0); + if (thd->spcont && thd->spcont->handle_error(error, MYSQL_ERROR::WARN_LEVEL_ERROR, thd)) { @@ -2480,6 +2603,7 @@ static int my_message_sql(uint error, const char *str, myf MyFlags) } +#ifndef EMBEDDED_LIBRARY static void *my_str_malloc_mysqld(size_t size) { return my_malloc(size, MYF(MY_FAE)); @@ -2490,22 +2614,11 @@ static void my_str_free_mysqld(void *ptr) { my_free((gptr)ptr, MYF(MY_FAE)); } +#endif /* EMBEDDED_LIBRARY */ #ifdef __WIN__ -struct utsname -{ - char nodename[FN_REFLEN]; -}; - - -int uname(struct utsname *a) -{ - return -1; -} - - pthread_handler_t handle_shutdown(void *arg) { MSG msg; @@ -2596,7 +2709,15 @@ static int init_common_variables(const char *conf_file_name, int argc, tzset(); // Set tzname max_system_variables.pseudo_thread_id= (ulong)~0; - start_time=time((time_t*) 0); + server_start_time= time((time_t*) 0); + rpl_filter= new Rpl_filter; + binlog_filter= new Rpl_filter; + if (!rpl_filter || !binlog_filter) + { + sql_perror("Could not allocate replication and binlog filters"); + exit(1); + } + if (init_thread_environment()) return 1; mysql_init_variables(); @@ -2604,7 +2725,7 @@ static int init_common_variables(const char *conf_file_name, int argc, #ifdef HAVE_TZNAME { struct tm tm_tmp; - localtime_r(&start_time,&tm_tmp); + localtime_r(&server_start_time,&tm_tmp); strmake(system_time_zone, tzname[tm_tmp.tm_isdst != 0 ? 1 : 0], sizeof(system_time_zone)-1); @@ -2626,9 +2747,15 @@ static int init_common_variables(const char *conf_file_name, int argc, */ mysql_bin_log.init_pthread_objects(); - if (gethostname(glob_hostname,sizeof(glob_hostname)-4) < 0) - strmov(glob_hostname,"mysql"); - strmake(pidfile_name, glob_hostname, sizeof(pidfile_name)-5); + if (gethostname(glob_hostname,sizeof(glob_hostname)) < 0) + { + strmake(glob_hostname, STRING_WITH_LEN("localhost")); + sql_print_warning("gethostname failed, using '%s' as hostname", + glob_hostname); + strmake(pidfile_name, STRING_WITH_LEN("mysql")); + } + else + strmake(pidfile_name, glob_hostname, sizeof(pidfile_name)-5); strmov(fn_ext(pidfile_name),".pid"); // Add proper extension /* @@ -2663,19 +2790,43 @@ static int init_common_variables(const char *conf_file_name, int argc, /* connections and databases needs lots of files */ { - uint files, wanted_files; + uint files, wanted_files, max_open_files; - wanted_files= 10+(uint) max(max_connections*5, - max_connections+table_cache_size*2); - set_if_bigger(wanted_files, open_files_limit); - files= my_set_max_open_files(wanted_files); + /* MyISAM requires two file handles per table. */ + wanted_files= 10+max_connections+table_cache_size*2; + /* + We are trying to allocate no less than max_connections*5 file + handles (i.e. we are trying to set the limit so that they will + be available). In addition, we allocate no less than how much + was already allocated. However below we report a warning and + recompute values only if we got less file handles than were + explicitly requested. No warning and re-computation occur if we + can't get max_connections*5 but still got no less than was + requested (value of wanted_files). + */ + max_open_files= max(max(wanted_files, max_connections*5), + open_files_limit); + files= my_set_max_open_files(max_open_files); if (files < wanted_files) { if (!open_files_limit) { - max_connections= (ulong) min((files-10),max_connections); - table_cache_size= (ulong) max((files-10-max_connections)/2,64); + /* + If we have requested too much file handles than we bring + max_connections in supported bounds. + */ + max_connections= (ulong) min(files-10-TABLE_OPEN_CACHE_MIN*2, + max_connections); + /* + Decrease table_cache_size according to max_connections, but + not below TABLE_OPEN_CACHE_MIN. Outer min() ensures that we + never increase table_cache_size automatically (that could + happen if max_connections is decreased above). + */ + table_cache_size= (ulong) min(max((files-10-max_connections)/2, + TABLE_OPEN_CACHE_MIN), + table_cache_size); DBUG_PRINT("warning", ("Changed limits: max_open_files: %u max_connections: %ld table_cache: %ld", files, max_connections, table_cache_size)); @@ -2693,16 +2844,41 @@ static int init_common_variables(const char *conf_file_name, int argc, return 1; init_client_errs(); lex_init(); + if (item_create_init()) + return 1; item_init(); set_var_init(); mysys_uses_curses=0; #ifdef USE_REGEX my_regex_init(&my_charset_latin1); #endif - if (!(default_charset_info= get_charset_by_csname(default_character_set_name, - MY_CS_PRIMARY, - MYF(MY_WME)))) - return 1; + /* + Process a comma-separated character set list and choose + the first available character set. This is mostly for + test purposes, to be able to start "mysqld" even if + the requested character set is not available (see bug#18743). + */ + for (;;) + { + char *next_character_set_name= strchr(default_character_set_name, ','); + if (next_character_set_name) + *next_character_set_name++= '\0'; + if (!(default_charset_info= + get_charset_by_csname(default_character_set_name, + MY_CS_PRIMARY, MYF(MY_WME)))) + { + if (next_character_set_name) + { + default_character_set_name= next_character_set_name; + default_collation_name= 0; // Ignore collation + } + else + return 1; // Eof of the list + } + else + break; + } + if (default_collation_name) { CHARSET_INFO *default_collation; @@ -2752,13 +2928,13 @@ static int init_common_variables(const char *conf_file_name, int argc, !(log_output_options & LOG_NONE)) sql_print_warning("Although a path was specified for the " "--log option, log tables are used. " - "To enable logging to file use the --log-output option."); + "To enable logging to files use the --log-output option."); if (opt_slow_log && opt_slow_logname && !(log_output_options & LOG_FILE) && !(log_output_options & LOG_NONE)) sql_print_warning("Although a path was specified for the " "--log-slow-queries option, log tables are used. " - "To enable logging to file use the --log-output option."); + "To enable logging to files use the --log-output option."); if (!opt_logname) opt_logname= make_default_log_name(buff, ".log"); @@ -2968,6 +3144,8 @@ static void openssl_lock(int mode, openssl_lock_t *lock, const char *file, #endif /* HAVE_OPENSSL */ +#ifndef EMBEDDED_LIBRARY + static void init_ssl() { #ifdef HAVE_OPENSSL @@ -2981,12 +3159,12 @@ static void init_ssl() if (!ssl_acceptor_fd) { opt_use_ssl = 0; - have_openssl= SHOW_OPTION_DISABLED; + have_ssl= SHOW_OPTION_DISABLED; } } else { - have_openssl= SHOW_OPTION_DISABLED; + have_ssl= SHOW_OPTION_DISABLED; } if (des_key_file) load_des_key_file(des_key_file); @@ -3005,6 +3183,8 @@ static void end_ssl() #endif /* HAVE_OPENSSL */ } +#endif /* EMBEDDED_LIBRARY */ + static int init_server_components() { @@ -3020,7 +3200,7 @@ static int init_server_components() query_cache_set_min_res_unit(query_cache_min_res_unit); query_cache_init(); query_cache_resize(query_cache_size); - randominit(&sql_rand,(ulong) start_time,(ulong) start_time/2); + randominit(&sql_rand,(ulong) server_start_time,(ulong) server_start_time/2); reset_floating_point_exceptions(); init_thr_lock(); #ifdef HAVE_REPLICATION @@ -3033,7 +3213,7 @@ static int init_server_components() if (opt_error_log) { if (!log_error_file_ptr[0]) - fn_format(log_error_file, glob_hostname, mysql_data_home, ".err", + fn_format(log_error_file, pidfile_name, mysql_data_home, ".err", MY_REPLACE_EXT); /* replace '.<domain>' by '.err', bug#4997 */ else fn_format(log_error_file, log_error_file_ptr, mysql_data_home, ".err", @@ -3110,30 +3290,28 @@ with --log-bin instead."); } if (opt_log_slave_updates && !opt_bin_log) { - sql_print_warning("You need to use --log-bin to make " - "--log-slave-updates work."); - unireg_abort(1); - } - - if (!opt_bin_log && (global_system_variables.binlog_format != BINLOG_FORMAT_UNSPEC)) - { - sql_print_warning("You need to use --log-bin to make " - "--binlog-format work."); + sql_print_error("You need to use --log-bin to make " + "--log-slave-updates work."); unireg_abort(1); } - if (global_system_variables.binlog_format == BINLOG_FORMAT_UNSPEC) - { -#if defined(HAVE_NDB_BINLOG) && defined(HAVE_ROW_BASED_REPLICATION) - if (opt_bin_log && have_ndbcluster == SHOW_OPTION_YES) - global_system_variables.binlog_format= BINLOG_FORMAT_ROW; + if (!opt_bin_log) + if (opt_binlog_format_id != BINLOG_FORMAT_UNSPEC) + { + sql_print_error("You need to use --log-bin to make " + "--binlog-format work."); + unireg_abort(1); + } else -#endif -#if defined(HAVE_ROW_BASED_REPLICATION) + { + global_system_variables.binlog_format= BINLOG_FORMAT_UNSPEC; + } + else + if (opt_binlog_format_id == BINLOG_FORMAT_UNSPEC) global_system_variables.binlog_format= BINLOG_FORMAT_MIXED; -#else - global_system_variables.binlog_format= BINLOG_FORMAT_STMT; -#endif - } + else + { + DBUG_ASSERT(global_system_variables.binlog_format != BINLOG_FORMAT_UNSPEC); + } /* Check that we have not let the format to unspecified at this point */ DBUG_ASSERT((uint)global_system_variables.binlog_format <= @@ -3187,7 +3365,7 @@ server."); using_update_log=1; } - if (plugin_init(0)) + if (plugin_init(opt_bootstrap)) { sql_print_error("Failed to init plugins."); return 1; @@ -3269,7 +3447,7 @@ server."); (TC_LOG *) &tc_log_mmap) : (TC_LOG *) &tc_log_dummy); - if (tc_log->open(opt_bin_logname)) + if (tc_log->open(opt_bin_log ? opt_bin_logname : opt_tc_log_file)) { sql_print_error("Can't init tc log"); unireg_abort(1); @@ -3287,7 +3465,7 @@ server."); #ifdef HAVE_REPLICATION if (opt_bin_log && expire_logs_days) { - long purge_time= time(0) - expire_logs_days*24*60*60; + long purge_time= (long) (time(0) - expire_logs_days*24*60*60); if (purge_time >= 0) mysql_bin_log.purge_logs_before_date(purge_time); } @@ -3332,6 +3510,8 @@ server."); } +#ifndef EMBEDDED_LIBRARY + static void create_maintenance_thread() { if (flush_time && flush_time != ~(ulong) 0L) @@ -3345,7 +3525,6 @@ static void create_maintenance_thread() static void create_shutdown_thread() { -#if !defined(EMBEDDED_LIBRARY) #ifdef __WIN__ hEventShutdown=CreateEvent(0, FALSE, FALSE, shutdown_event_name); pthread_t hThread; @@ -3354,10 +3533,11 @@ static void create_shutdown_thread() // On "Stop Service" we have to do regular shutdown Service.SetShutdownEvent(hEventShutdown); -#endif -#endif // EMBEDDED_LIBRARY +#endif /* __WIN__ */ } +#endif /* EMBEDDED_LIBRARY */ + #if (defined(__NT__) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY) static void handle_connections_methods() @@ -3437,15 +3617,8 @@ int win_main(int argc, char **argv) int main(int argc, char **argv) #endif { - rpl_filter= new Rpl_filter; - binlog_filter= new Rpl_filter; - if (!rpl_filter || !binlog_filter) - { - sql_perror("Could not allocate replication and binlog filters"); - exit(1); - } - MY_INIT(argv[0]); // init my_sys library & pthreads + /* nothing should come before this line ^^^ */ /* Perform basic logger initialization logger. Should be called after @@ -3505,8 +3678,8 @@ int main(int argc, char **argv) if (stack_size && stack_size < thread_stack) { if (global_system_variables.log_warnings) - sql_print_warning("Asked for %ld thread stack, but got %ld", - thread_stack, stack_size); + sql_print_warning("Asked for %lu thread stack, but got %ld", + thread_stack, (long) stack_size); #if defined(__ia64__) || defined(__ia64) thread_stack= stack_size*2; #else @@ -3598,7 +3771,7 @@ we force server id to 2, but this MySQL server will not act as a slave."); error_handler_hook= my_message_sql; start_signal_handler(); // Creates pidfile - if (acl_init(opt_noacl) || + if (mysql_rm_tmp_tables() || acl_init(opt_noacl) || my_tz_init((THD *)0, default_tz_name, opt_bootstrap)) { abort_loop=1; @@ -3617,6 +3790,9 @@ we force server id to 2, but this MySQL server will not act as a slave."); if (!opt_noacl) (void) grant_init(); + if (!opt_bootstrap) + servers_init(0); + if (!opt_noacl) { #ifdef HAVE_DLOPEN @@ -3664,15 +3840,18 @@ we force server id to 2, but this MySQL server will not act as a slave."); mysqld_port, MYSQL_COMPILATION_COMMENT); - // Signal threads waiting for server to be started - mysqld_server_started= 1; - pthread_cond_signal(&COND_server_started); - if (!opt_noacl) { if (Events::get_instance()->init()) unireg_abort(1); } + + /* Signal threads waiting for server to be started */ + pthread_mutex_lock(&LOCK_server_started); + mysqld_server_started= 1; + pthread_cond_signal(&COND_server_started); + pthread_mutex_unlock(&LOCK_server_started); + #if defined(__NT__) || defined(HAVE_SMEM) handle_connections_methods(); #else @@ -3938,7 +4117,7 @@ static void bootstrap(FILE *file) my_net_init(&thd->net,(st_vio*) 0); thd->max_client_packet_length= thd->net.max_packet; thd->security_ctx->master_access= ~(ulong)0; - thd->thread_id=thread_id++; + thd->thread_id= thd->variables.pseudo_thread_id= thread_id++; thread_count++; bootstrap_file=file; @@ -3981,6 +4160,74 @@ static bool read_init_file(char *file_name) #ifndef EMBEDDED_LIBRARY + +/* + Simple scheduler that use the main thread to handle the request + + NOTES + This is only used for debugging, when starting mysqld with + --thread-handling=no-threads or --one-thread + + When we enter this function, LOCK_thread_count is hold! +*/ + +void handle_connection_in_main_thread(THD *thd) +{ + safe_mutex_assert_owner(&LOCK_thread_count); + thread_cache_size=0; // Safety + threads.append(thd); + (void) pthread_mutex_unlock(&LOCK_thread_count); + handle_one_connection((void*) thd); +} + + +/* + Scheduler that uses one thread per connection +*/ + +void create_thread_to_handle_connection(THD *thd) +{ + if (cached_thread_count > wake_thread) + { + /* Get thread from cache */ + thread_cache.append(thd); + wake_thread++; + pthread_cond_signal(&COND_thread_cache); + } + else + { + /* Create new thread to handle connection */ + int error; + thread_created++; + threads.append(thd); + DBUG_PRINT("info",(("creating thread %lu"), thd->thread_id)); + thd->connect_time = time(NULL); + if ((error=pthread_create(&thd->real_id,&connection_attrib, + handle_one_connection, + (void*) thd))) + { + /* purify: begin inspected */ + DBUG_PRINT("error", + ("Can't create thread to handle request (error %d)", + error)); + thread_count--; + thd->killed= THD::KILL_CONNECTION; // Safety + (void) pthread_mutex_unlock(&LOCK_thread_count); + statistic_increment(aborted_connects,&LOCK_status); + net_printf_error(thd, ER_CANT_CREATE_THREAD, error); + (void) pthread_mutex_lock(&LOCK_thread_count); + close_connection(thd,0,0); + delete thd; + (void) pthread_mutex_unlock(&LOCK_thread_count); + return; + /* purecov: end */ + } + } + (void) pthread_mutex_unlock(&LOCK_thread_count); + DBUG_PRINT("info",("Thread created")); +} + + /* Create new thread to handle incoming connection. @@ -4002,10 +4249,9 @@ static bool read_init_file(char *file_name) static void create_new_thread(THD *thd) { + NET *net=&thd->net; DBUG_ENTER("create_new_thread"); - NET *net=&thd->net; // For easy ref - net->read_timeout = (uint) connect_timeout; if (protocol_version > 9) net->return_errno=1; @@ -4018,64 +4264,15 @@ static void create_new_thread(THD *thd) DBUG_VOID_RETURN; } pthread_mutex_lock(&LOCK_thread_count); - thd->thread_id=thread_id++; - - thd->real_id=pthread_self(); // Keep purify happy + thd->thread_id= thd->variables.pseudo_thread_id= thread_id++; /* Start a new thread to handle connection */ thread_count++; -#ifdef ONE_THREAD - if (test_flags & TEST_NO_THREADS) // For debugging under Linux - { - thread_cache_size=0; // Safety - threads.append(thd); - thd->real_id=pthread_self(); - (void) pthread_mutex_unlock(&LOCK_thread_count); - handle_one_connection((void*) thd); - } - else -#endif - { - if (thread_count-delayed_insert_threads > max_used_connections) - max_used_connections=thread_count-delayed_insert_threads; - - if (cached_thread_count > wake_thread) - { - thread_cache.append(thd); - wake_thread++; - pthread_cond_signal(&COND_thread_cache); - } - else - { - int error; - thread_created++; - threads.append(thd); - DBUG_PRINT("info",(("creating thread %d"), thd->thread_id)); - thd->connect_time = time(NULL); - if ((error=pthread_create(&thd->real_id,&connection_attrib, - handle_one_connection, - (void*) thd))) - { - DBUG_PRINT("error", - ("Can't create thread to handle request (error %d)", - error)); - thread_count--; - thd->killed= THD::KILL_CONNECTION; // Safety - (void) pthread_mutex_unlock(&LOCK_thread_count); - statistic_increment(aborted_connects,&LOCK_status); - net_printf_error(thd, ER_CANT_CREATE_THREAD, error); - (void) pthread_mutex_lock(&LOCK_thread_count); - close_connection(thd,0,0); - delete thd; - (void) pthread_mutex_unlock(&LOCK_thread_count); - DBUG_VOID_RETURN; - } - } - (void) pthread_mutex_unlock(&LOCK_thread_count); + if (thread_count - delayed_insert_threads > max_used_connections) + max_used_connections= thread_count - delayed_insert_threads; - } - DBUG_PRINT("info",("Thread created")); + thread_scheduler.add_connection(thd); DBUG_VOID_RETURN; } #endif /* EMBEDDED_LIBRARY */ @@ -4299,12 +4496,7 @@ pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused))) } if (sock == unix_sock) thd->security_ctx->host=(char*) my_localhost; -#ifdef __WIN__ - /* Set default wait_timeout */ - ulong wait_timeout= global_system_variables.net_wait_timeout * 1000; - (void) setsockopt(new_sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&wait_timeout, - sizeof(wait_timeout)); -#endif + create_new_thread(thd); } @@ -4670,9 +4862,7 @@ enum options_mysqld #ifndef DBUG_OFF OPT_BINLOG_SHOW_XID, #endif -#ifdef HAVE_ROW_BASED_REPLICATION OPT_BINLOG_ROWS_EVENT_MAX_SIZE, -#endif OPT_WANT_CORE, OPT_CONCURRENT_INSERT, OPT_MEMLOCK, OPT_MYISAM_RECOVER, OPT_REPLICATE_REWRITE_DB, OPT_SERVER_ID, @@ -4697,14 +4887,13 @@ enum options_mysqld OPT_LOG_BIN_TRUST_FUNCTION_CREATORS, OPT_SAFE_SHOW_DB, OPT_INNODB_SAFE_BINLOG, OPT_INNODB, OPT_ISAM, - OPT_ENGINE_CONDITION_PUSHDOWN, - OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, OPT_NDB_USE_EXACT_COUNT, + OPT_ENGINE_CONDITION_PUSHDOWN, OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, + OPT_NDB_USE_EXACT_COUNT, OPT_NDB_USE_TRANSACTIONS, OPT_NDB_FORCE_SEND, OPT_NDB_AUTOINCREMENT_PREFETCH_SZ, OPT_NDB_SHM, OPT_NDB_OPTIMIZED_NODE_SELECTION, OPT_NDB_CACHE_CHECK_TIME, OPT_NDB_MGMD, OPT_NDB_NODEID, OPT_NDB_DISTRIBUTION, OPT_NDB_INDEX_STAT_ENABLE, - OPT_NDB_INDEX_STAT_CACHE_ENTRIES, OPT_NDB_INDEX_STAT_UPDATE_FREQ, OPT_NDB_EXTRA_LOGGING, OPT_NDB_REPORT_THRESH_BINLOG_EPOCH_SLIP, OPT_NDB_REPORT_THRESH_BINLOG_MEM_USAGE, @@ -4783,6 +4972,7 @@ enum options_mysqld OPT_INNODB_SYNC_SPIN_LOOPS, OPT_INNODB_CONCURRENCY_TICKETS, OPT_INNODB_THREAD_SLEEP_DELAY, + OPT_INNODB_STATS_ON_METADATA, OPT_BDB_CACHE_SIZE, OPT_BDB_CACHE_PARTS, OPT_BDB_LOG_BUFFER_SIZE, @@ -4832,7 +5022,10 @@ enum options_mysqld OPT_PORT_OPEN_TIMEOUT, OPT_GENERAL_LOG, OPT_SLOW_LOG, - OPT_MERGE + OPT_MERGE, + OPT_THREAD_HANDLING, + OPT_INNODB_ROLLBACK_ON_TIMEOUT, + OPT_SECURE_FILE_PRIV }; @@ -4883,7 +5076,7 @@ struct my_option my_long_options[] = (gptr*) &my_bind_addr_str, (gptr*) &my_bind_addr_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"binlog_format", OPT_BINLOG_FORMAT, -#ifdef HAVE_ROW_BASED_REPLICATION + "Does not have any effect without '--log-bin'. " "Tell the master the form of binary logging to use: either 'row' for " "row-based binary logging, or 'statement' for statement-based binary " "logging, or 'mixed'. 'mixed' is statement-based binary logging except " @@ -4891,27 +5084,18 @@ struct my_option my_long_options[] = "involve user-defined functions (i.e. UDFs) or the UUID() function; for " "those, row-based binary logging is automatically used. " #ifdef HAVE_NDB_BINLOG - "If ndbcluster is enabled, the default is 'row'." + "If ndbcluster is enabled and binlog_format is `mixed', the format switches" + " to 'row' and back implicitly per each query accessing a NDB table." #endif -#else - "Tell the master the form of binary logging to use: this build " - "supports only statement-based binary logging, so only 'statement' is " - "a legal value." -#endif - , 0, 0, 0, GET_STR, REQUIRED_ARG, -#ifdef HAVE_ROW_BASED_REPLICATION - BINLOG_FORMAT_MIXED -#else - BINLOG_FORMAT_STMT -#endif - , 0, 0, 0, 0, 0 }, + ,(gptr*) &opt_binlog_format, (gptr*) &opt_binlog_format, + 0, GET_STR, REQUIRED_ARG, BINLOG_FORMAT_MIXED, BINLOG_FORMAT_STMT, + BINLOG_FORMAT_MIXED, 0, 0, 0}, {"binlog-do-db", OPT_BINLOG_DO_DB, "Tells the master it should log updates for the specified database, and exclude all others not explicitly mentioned.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"binlog-ignore-db", OPT_BINLOG_IGNORE_DB, "Tells the master that updates to the given database should not be logged tothe binary log.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, -#ifdef HAVE_ROW_BASED_REPLICATION {"binlog-row-event-max-size", OPT_BINLOG_ROWS_EVENT_MAX_SIZE, "The maximum size of a row-based binary log event in bytes. Rows will be " "grouped into events smaller than this size if possible. " @@ -4923,9 +5107,10 @@ struct my_option my_long_options[] = /* sub_size */ 0, /* block_size */ 256, /* app_type */ 0 }, -#endif +#ifndef DISABLE_GRANT_OPTIONS {"bootstrap", OPT_BOOTSTRAP, "Used by mysql installation scripts.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, +#endif {"character-set-client-handshake", OPT_CHARACTER_SET_CLIENT_HANDSHAKE, "Don't ignore client side character set value sent during handshake.", (gptr*) &opt_character_set_client_handshake, @@ -5019,7 +5204,7 @@ struct my_option my_long_options[] = "Push supported query conditions to the storage engine.", (gptr*) &global_system_variables.engine_condition_pushdown, (gptr*) &global_system_variables.engine_condition_pushdown, - 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, /* See how it's handled in get_one_option() */ {"event-scheduler", OPT_EVENT_SCHEDULER, "Enable/disable the event scheduler.", NULL, NULL, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, @@ -5048,9 +5233,11 @@ Disable with --skip-large-pages.", {"init-connect", OPT_INIT_CONNECT, "Command(s) that are executed for each new connection", (gptr*) &opt_init_connect, (gptr*) &opt_init_connect, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#ifndef DISABLE_GRANT_OPTIONS {"init-file", OPT_INIT_FILE, "Read SQL commands from this file at startup.", (gptr*) &opt_init_file, (gptr*) &opt_init_file, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#endif {"init-rpl-role", OPT_INIT_RPL_ROLE, "Set the replication role.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"init-slave", OPT_INIT_SLAVE, "Command(s) that are executed when a slave connects to this master", @@ -5125,10 +5312,18 @@ Disable with --skip-innodb-doublewrite.", (gptr*) &innobase_use_doublewrite, (gptr*) &srv_max_purge_lag, (gptr*) &srv_max_purge_lag, 0, GET_LONG, REQUIRED_ARG, 0, 0, ~0L, 0, 1L, 0}, + {"innodb_rollback_on_timeout", OPT_INNODB_ROLLBACK_ON_TIMEOUT, + "Roll back the complete transaction on lock wait timeout, for 4.x compatibility (disabled by default)", + (gptr*) &innobase_rollback_on_timeout, (gptr*) &innobase_rollback_on_timeout, + 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"innodb_status_file", OPT_INNODB_STATUS_FILE, "Enable SHOW INNODB STATUS output in the innodb_status.<pid> file", (gptr*) &innobase_create_status_file, (gptr*) &innobase_create_status_file, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"innodb_stats_on_metadata", OPT_INNODB_STATS_ON_METADATA, + "Enable statistics gathering for metadata commands such as SHOW TABLE STATUS (on by default)", + (gptr*) &innobase_stats_on_metadata, (gptr*) &innobase_stats_on_metadata, + 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {"innodb_support_xa", OPT_INNODB_SUPPORT_XA, "Enable InnoDB support for the XA two-phase commit", (gptr*) &global_system_variables.innodb_support_xa, @@ -5140,9 +5335,6 @@ Disable with --skip-innodb-doublewrite.", (gptr*) &innobase_use_doublewrite, (gptr*) &global_system_variables.innodb_table_locks, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, #endif /* End WITH_INNOBASE_STORAGE_ENGINE */ - {"isam", OPT_ISAM, "Obsolete. ISAM storage engine is no longer supported.", - (gptr*) &opt_isam, (gptr*) &opt_isam, 0, GET_BOOL, NO_ARG, 0, 0, 0, - 0, 0, 0}, {"language", 'L', "Client error messages in given language. May be given as a full path.", (gptr*) &language_ptr, (gptr*) &language_ptr, 0, GET_STR, REQUIRED_ARG, @@ -5184,11 +5376,9 @@ Disable with --skip-innodb-doublewrite.", (gptr*) &innobase_use_doublewrite, "If equal to 0 (the default), then when --log-bin is used, creation of " "a stored function (or trigger) is allowed only to users having the SUPER privilege " "and only if this stored function (trigger) may not break binary logging." -#ifdef HAVE_ROW_BASED_REPLICATION "Note that if ALL connections to this server ALWAYS use row-based binary " "logging, the security issues do not exist and the binary logging cannot " "break, so you can safely set this to 1." -#endif ,(gptr*) &trust_function_creators, (gptr*) &trust_function_creators, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"log-error", OPT_ERROR_LOG_FILE, "Error log file.", @@ -5314,9 +5504,6 @@ master-ssl", #endif /* HAVE_REPLICATION */ {"memlock", OPT_MEMLOCK, "Lock mysqld in memory.", (gptr*) &locked_in_memory, (gptr*) &locked_in_memory, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"merge", OPT_MERGE, "Enable Merge storage engine. Disable with \ ---skip-merge.", - (gptr*) &opt_merge, (gptr*) &opt_merge, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0}, {"myisam-recover", OPT_MYISAM_RECOVER, "Syntax: myisam-recover[=option[,option...]], where option can be DEFAULT, BACKUP, FORCE or QUICK.", (gptr*) &myisam_recover_options_str, (gptr*) &myisam_recover_options_str, 0, @@ -5396,6 +5583,17 @@ Disable with --skip-ndbcluster (will save memory).", (gptr*) &global_system_variables.ndb_use_exact_count, (gptr*) &global_system_variables.ndb_use_exact_count, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, + {"ndb-use-transactions", OPT_NDB_USE_TRANSACTIONS, + "Use transactions for large inserts, if enabled then large " + "inserts will be split into several smaller transactions", + (gptr*) &global_system_variables.ndb_use_transactions, + (gptr*) &global_system_variables.ndb_use_transactions, + 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, + {"ndb_use_transactions", OPT_NDB_USE_TRANSACTIONS, + "same as --ndb-use-transactions.", + (gptr*) &global_system_variables.ndb_use_transactions, + (gptr*) &global_system_variables.ndb_use_transactions, + 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, {"ndb-shm", OPT_NDB_SHM, "Use shared memory connections when available.", (gptr*) &opt_ndb_shm, @@ -5414,19 +5612,7 @@ Disable with --skip-ndbcluster (will save memory).", "Use ndb index statistics in query optimization.", (gptr*) &global_system_variables.ndb_index_stat_enable, (gptr*) &max_system_variables.ndb_index_stat_enable, - 0, GET_BOOL, OPT_ARG, 1, 0, 1, 0, 0, 0}, - {"ndb-index-stat-cache-entries", OPT_NDB_INDEX_STAT_CACHE_ENTRIES, - "Number of start/end keys to store in statistics memory cache." - " Zero means no cache and forces query of db nodes always.", - (gptr*) &global_system_variables.ndb_index_stat_cache_entries, - (gptr*) &max_system_variables.ndb_index_stat_cache_entries, - 0, GET_ULONG, OPT_ARG, 32, 0, ~0L, 0, 0, 0}, - {"ndb-index-stat-update-freq", OPT_NDB_INDEX_STAT_UPDATE_FREQ, - "How often, in the long run, to query db nodes instead of statistics cache." - " For example 20 means every 20th time.", - (gptr*) &global_system_variables.ndb_index_stat_update_freq, - (gptr*) &max_system_variables.ndb_index_stat_update_freq, - 0, GET_ULONG, OPT_ARG, 20, 0, ~0L, 0, 0, 0}, + 0, GET_BOOL, OPT_ARG, 0, 0, 1, 0, 0, 0}, #endif {"ndb-use-copying-alter-table", OPT_NDB_USE_COPYING_ALTER_TABLE, @@ -5452,11 +5638,9 @@ Disable with --skip-ndbcluster (will save memory).", (gptr*) &global_system_variables.old_passwords, (gptr*) &max_system_variables.old_passwords, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, -#ifdef ONE_THREAD {"one-thread", OPT_ONE_THREAD, - "Only use one thread (for debugging under Linux).", 0, 0, 0, GET_NO_ARG, - NO_ARG, 0, 0, 0, 0, 0, 0}, -#endif + "(deprecated): Only use one thread (for debugging under Linux). Use thread-handling=no-threads instead", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"old-style-user-limits", OPT_OLD_STYLE_USER_LIMITS, "Enable old-style user limits (before 5.0.3 user resources were counted per each user+host vs. per account)", (gptr*) &opt_old_style_user_limits, (gptr*) &opt_old_style_user_limits, @@ -5548,6 +5732,10 @@ Can't be set to 1 if --log-slave-updates is used.", {"secure-auth", OPT_SECURE_AUTH, "Disallow authentication for accounts that have old (pre-4.1) passwords.", (gptr*) &opt_secure_auth, (gptr*) &opt_secure_auth, 0, GET_BOOL, NO_ARG, my_bool(0), 0, 0, 0, 0, 0}, + {"secure-file-priv", OPT_SECURE_FILE_PRIV, + "Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to files within specified directory", + (gptr*) &opt_secure_file_priv, (gptr*) &opt_secure_file_priv, 0, + GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"server-id", OPT_SERVER_ID, "Uniquely identifies the server instance in the community of replication partners.", (gptr*) &server_id, (gptr*) &server_id, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 0, @@ -5569,10 +5757,12 @@ Can't be set to 1 if --log-slave-updates is used.", "Show user and password in SHOW SLAVE HOSTS on this master", (gptr*) &opt_show_slave_auth_info, (gptr*) &opt_show_slave_auth_info, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, +#ifndef DISABLE_GRANT_OPTIONS {"skip-grant-tables", OPT_SKIP_GRANT, "Start without grant tables. This gives all users FULL ACCESS to all tables!", (gptr*) &opt_noacl, (gptr*) &opt_noacl, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, +#endif {"skip-host-cache", OPT_SKIP_HOST_CACHE, "Don't cache host names.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"skip-locking", OPT_SKIP_LOCK, @@ -5605,8 +5795,8 @@ Can't be set to 1 if --log-slave-updates is used.", {"skip-symlink", OPT_SKIP_SYMLINKS, "Don't allow symlinking of tables. Deprecated option. Use --skip-symbolic-links instead.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"skip-thread-priority", OPT_SKIP_PRIOR, - "Don't give threads different priorities.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, - 0, 0, 0, 0, 0}, + "Don't give threads different priorities.", 0, 0, 0, GET_NO_ARG, NO_ARG, + DEFAULT_SKIP_THREAD_PRIORITY, 0, 0, 0, 0, 0}, #ifdef HAVE_REPLICATION {"slave-load-tmpdir", OPT_SLAVE_LOAD_TMPDIR, "The location where the slave should put its temporary files when \ @@ -5680,6 +5870,11 @@ log and this option does nothing anymore.", 0, 0, 0, 0, 0}, {"use-symbolic-links", 's', "Enable symbolic link support. Deprecated option; use --symbolic-links instead.", (gptr*) &my_use_symdir, (gptr*) &my_use_symdir, 0, GET_BOOL, NO_ARG, + /* + The system call realpath() produces warnings under valgrind and + purify. These are not suppressed: instead we disable symlinks + option if compiled with valgrind support. + */ IF_PURIFY(0,1), 0, 0, 0, 0, 0}, {"user", 'u', "Run mysqld daemon as user.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -5787,10 +5982,6 @@ log and this option does nothing anymore.", (gptr*) &srv_auto_extend_increment, (gptr*) &srv_auto_extend_increment, 0, GET_LONG, REQUIRED_ARG, 8L, 1L, 1000L, 0, 1L, 0}, - {"innodb_buffer_pool_awe_mem_mb", OPT_INNODB_BUFFER_POOL_AWE_MEM_MB, - "If Windows AWE is used, the size of InnoDB buffer pool allocated from the AWE memory.", - (gptr*) &innobase_buffer_pool_awe_mem_mb, (gptr*) &innobase_buffer_pool_awe_mem_mb, 0, - GET_LONG, REQUIRED_ARG, 0, 0, 63000, 0, 1, 0}, {"innodb_buffer_pool_size", OPT_INNODB_BUFFER_POOL_SIZE, "The size of the memory buffer InnoDB uses to cache data and indexes of its tables.", (gptr*) &innobase_buffer_pool_size, (gptr*) &innobase_buffer_pool_size, 0, @@ -5928,9 +6119,11 @@ The minimum value for this variable is 4096.", "If there is more than this number of interrupted connections from a host this host will be blocked from further connections.", (gptr*) &max_connect_errors, (gptr*) &max_connect_errors, 0, GET_ULONG, REQUIRED_ARG, MAX_CONNECT_ERRORS, 1, ~0L, 0, 1, 0}, + // Default max_connections of 151 is larger than Apache's default max + // children, to avoid "too many connections" error in a common setup {"max_connections", OPT_MAX_CONNECTIONS, "The number of simultaneous clients allowed.", (gptr*) &max_connections, - (gptr*) &max_connections, 0, GET_ULONG, REQUIRED_ARG, 100, 1, 16384, 0, 1, + (gptr*) &max_connections, 0, GET_ULONG, REQUIRED_ARG, 151, 1, 100000, 0, 1, 0}, {"max_delayed_threads", OPT_MAX_DELAYED_THREADS, "Don't start more than this number of threads to handle INSERT DELAYED statements. If set to zero, which means INSERT DELAYED is not used.", @@ -5945,8 +6138,9 @@ The minimum value for this variable is 4096.", {"max_heap_table_size", OPT_MAX_HEP_TABLE_SIZE, "Don't allow creation of heap tables bigger than this.", (gptr*) &global_system_variables.max_heap_table_size, - (gptr*) &max_system_variables.max_heap_table_size, 0, GET_ULONG, - REQUIRED_ARG, 16*1024*1024L, 16384, ~0L, MALLOC_OVERHEAD, 1024, 0}, + (gptr*) &max_system_variables.max_heap_table_size, 0, GET_ULL, + REQUIRED_ARG, 16*1024*1024L, 16384, MAX_MEM_TABLE_SIZE, + MALLOC_OVERHEAD, 1024, 0}, {"max_join_size", OPT_MAX_JOIN_SIZE, "Joins that are probably going to read more than max_join_size records return an error.", (gptr*) &global_system_variables.max_join_size, @@ -6198,15 +6392,15 @@ The minimum value for this variable is 4096.", {"table_cache", OPT_TABLE_OPEN_CACHE, "Deprecated; use --table_open_cache instead.", (gptr*) &table_cache_size, (gptr*) &table_cache_size, 0, GET_ULONG, - REQUIRED_ARG, 64, 1, 512*1024L, 0, 1, 0}, + REQUIRED_ARG, TABLE_OPEN_CACHE_DEFAULT, 1, 512*1024L, 0, 1, 0}, {"table_definition_cache", OPT_TABLE_DEF_CACHE, "The number of cached table definitions.", (gptr*) &table_def_size, (gptr*) &table_def_size, 0, GET_ULONG, REQUIRED_ARG, 128, 1, 512*1024L, 0, 1, 0}, {"table_open_cache", OPT_TABLE_OPEN_CACHE, "The number of cached open tables.", - (gptr*) &table_cache_size, (gptr*) &table_cache_size, - 0, GET_ULONG, REQUIRED_ARG, 64, 1, 512*1024L, 0, 1, 0}, + (gptr*) &table_cache_size, (gptr*) &table_cache_size, 0, GET_ULONG, + REQUIRED_ARG, TABLE_OPEN_CACHE_DEFAULT, 1, 512*1024L, 0, 1, 0}, {"table_lock_wait_timeout", OPT_TABLE_LOCK_WAIT_TIMEOUT, "Timeout in seconds to wait for a table level lock before returning an " "error. Used only if the connection has active cursors.", @@ -6220,6 +6414,12 @@ The minimum value for this variable is 4096.", "Permits the application to give the threads system a hint for the desired number of threads that should be run at the same time.", (gptr*) &concurrency, (gptr*) &concurrency, 0, GET_ULONG, REQUIRED_ARG, DEFAULT_CONCURRENCY, 1, 512, 0, 1, 0}, +#if HAVE_POOL_OF_THREADS == 1 + {"thread_pool_size", OPT_THREAD_CACHE_SIZE, + "How many threads we should create to handle query requests in case of 'thread_handling=pool-of-threads'", + (gptr*) &thread_pool_size, (gptr*) &thread_pool_size, 0, GET_ULONG, + REQUIRED_ARG, 20, 1, 16384, 0, 1, 0}, +#endif {"thread_stack", OPT_THREAD_STACK, "The stack size for each thread.", (gptr*) &thread_stack, (gptr*) &thread_stack, 0, GET_ULONG, REQUIRED_ARG,DEFAULT_THREAD_STACK, @@ -6232,8 +6432,8 @@ The minimum value for this variable is 4096.", {"tmp_table_size", OPT_TMP_TABLE_SIZE, "If an in-memory temporary table exceeds this size, MySQL will automatically convert it to an on-disk MyISAM table.", (gptr*) &global_system_variables.tmp_table_size, - (gptr*) &max_system_variables.tmp_table_size, 0, GET_ULONG, - REQUIRED_ARG, 16*1024*1024L, 1024, ~0L, 0, 1, 0}, /* See max_heap_table_size . */ + (gptr*) &max_system_variables.tmp_table_size, 0, GET_ULL, + REQUIRED_ARG, 16*1024*1024L, 1024, MAX_MEM_TABLE_SIZE, 0, 1, 0}, {"transaction_alloc_block_size", OPT_TRANS_ALLOC_BLOCK_SIZE, "Allocation block size for transactions to be stored in binary log", (gptr*) &global_system_variables.trans_alloc_block_size, @@ -6244,6 +6444,10 @@ The minimum value for this variable is 4096.", (gptr*) &global_system_variables.trans_prealloc_size, (gptr*) &max_system_variables.trans_prealloc_size, 0, GET_ULONG, REQUIRED_ARG, TRANS_ALLOC_PREALLOC_SIZE, 1024, ~0L, 0, 1024, 0}, + {"thread_handling", OPT_THREAD_HANDLING, + "Define threads usage for handling queries: " + "one-thread-per-connection or no-threads", 0, 0, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"updatable_views_with_limit", OPT_UPDATABLE_VIEWS_WITH_LIMIT, "1 = YES = Don't issue an error message (warning only) if a VIEW without presence of a key of the underlying table is used in queries with a LIMIT clause for updating. 0 = NO = Prohibit update of a VIEW, which does not contain a key of the underlying table and the query uses a LIMIT clause (usually get from GUI tools).", (gptr*) &global_system_variables.updatable_views_with_limit, @@ -6276,7 +6480,7 @@ static int show_starttime(THD *thd, SHOW_VAR *var, char *buff) { var->type= SHOW_LONG; var->value= buff; - *((long *)buff)= (long) (thd->query_start() - start_time); + *((long *)buff)= (long) (thd->query_start() - server_start_time); return 0; } @@ -6328,6 +6532,16 @@ static int show_open_tables(THD *thd, SHOW_VAR *var, char *buff) return 0; } +static int show_prepared_stmt_count(THD *thd, SHOW_VAR *var, char *buff) +{ + var->type= SHOW_LONG; + var->value= buff; + pthread_mutex_lock(&LOCK_prepared_stmt_count); + *((long *)buff)= (long)prepared_stmt_count; + pthread_mutex_unlock(&LOCK_prepared_stmt_count); + return 0; +} + static int show_table_definitions(THD *thd, SHOW_VAR *var, char *buff) { var->type= SHOW_LONG; @@ -6581,6 +6795,10 @@ static int show_ssl_get_cipher_list(THD *thd, SHOW_VAR *var, char *buff) #endif /* HAVE_OPENSSL */ +/* + Variables shown by SHOW STATUS in alphabetical order +*/ + SHOW_VAR status_vars[]= { {"Aborted_clients", (char*) &aborted_threads, SHOW_LONG}, {"Aborted_connects", (char*) &aborted_connects, SHOW_LONG}, @@ -6605,6 +6823,7 @@ SHOW_VAR status_vars[]= { {"Com_create_function", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_FUNCTION]), SHOW_LONG_STATUS}, {"Com_create_index", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_INDEX]), SHOW_LONG_STATUS}, {"Com_create_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_TABLE]), SHOW_LONG_STATUS}, + {"Com_create_user", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_USER]), SHOW_LONG_STATUS}, {"Com_dealloc_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DEALLOCATE_PREPARE]), SHOW_LONG_STATUS}, {"Com_delete", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DELETE]), SHOW_LONG_STATUS}, {"Com_delete_multi", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DELETE_MULTI]), SHOW_LONG_STATUS}, @@ -6734,6 +6953,7 @@ SHOW_VAR status_vars[]= { {"Open_table_definitions", (char*) &show_table_definitions, SHOW_FUNC}, {"Open_tables", (char*) &show_open_tables, SHOW_FUNC}, {"Opened_tables", (char*) offsetof(STATUS_VAR, opened_tables), SHOW_LONG_STATUS}, + {"Prepared_stmt_count", (char*) &show_prepared_stmt_count, SHOW_FUNC}, #ifdef HAVE_QUERY_CACHE {"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, SHOW_LONG_NOFLUSH}, {"Qcache_free_memory", (char*) &query_cache.free_memory, SHOW_LONG_NOFLUSH}, @@ -6892,6 +7112,7 @@ static void mysql_init_variables(void) opt_logname= opt_update_logname= opt_binlog_index_name= opt_slow_logname= 0; opt_tc_log_file= (char *)"tc.log"; // no hostname in tc_log file name ! opt_secure_auth= 0; + opt_secure_file_priv= 0; opt_bootstrap= opt_myisam_log= 0; mqh_used= 0; segfaulted= kill_in_progress= 0; @@ -6913,6 +7134,7 @@ static void mysql_init_variables(void) binlog_cache_use= binlog_cache_disk_use= 0; max_used_connections= slow_launch_threads = 0; mysqld_user= mysqld_chroot= opt_init_file= opt_bin_logname = 0; + prepared_stmt_count= 0; errmesg= 0; mysqld_unix_port= opt_mysql_tmpdir= my_bind_addr_str= NullS; bzero((gptr) &mysql_tmpdir_list, sizeof(mysql_tmpdir_list)); @@ -6942,8 +7164,8 @@ static void mysql_init_variables(void) OPTION_QUOTE_SHOW_CREATE | OPTION_SQL_NOTES); protocol_version= PROTOCOL_VERSION; what_to_log= ~ (1L << (uint) COM_TIME); - refresh_version= flush_version= 1L; /* Increments on each reload */ - query_id= thread_id= 1L; + refresh_version= 1L; /* Increments on each reload */ + global_query_id= thread_id= 1L; strmov(server_version, MYSQL_SERVER_VERSION); myisam_recover_options_str= sql_mode_str= "OFF"; myisam_stats_method_str= "nulls_unequal"; @@ -7003,36 +7225,11 @@ static void mysql_init_variables(void) "d:t:i:o,/tmp/mysqld.trace"); #endif opt_error_log= IF_WIN(1,0); -#ifdef WITH_MYISAMMRG_STORAGE_ENGINE - have_merge_db= SHOW_OPTION_YES; -#else - have_merge_db= SHOW_OPTION_NO; -#endif #ifdef WITH_INNOBASE_STORAGE_ENGINE have_innodb= SHOW_OPTION_YES; #else have_innodb= SHOW_OPTION_NO; #endif -#ifdef WITH_EXAMPLE_STORAGE_ENGINE - have_example_db= SHOW_OPTION_YES; -#else - have_example_db= SHOW_OPTION_NO; -#endif -#ifdef WITH_ARCHIVE_STORAGE_ENGINE - have_archive_db= SHOW_OPTION_YES; -#else - have_archive_db= SHOW_OPTION_NO; -#endif -#ifdef WITH_BLACKHOLE_STORAGE_ENGINE - have_blackhole_db= SHOW_OPTION_YES; -#else - have_blackhole_db= SHOW_OPTION_NO; -#endif -#ifdef WITH_FEDERATED_STORAGE_ENGINE - have_federated_db= SHOW_OPTION_YES; -#else - have_federated_db= SHOW_OPTION_NO; -#endif #ifdef WITH_CSV_STORAGE_ENGINE have_csv_db= SHOW_OPTION_YES; #else @@ -7048,14 +7245,9 @@ static void mysql_init_variables(void) #else have_partition_db= SHOW_OPTION_NO; #endif -#ifdef HAVE_ROW_BASED_REPLICATION - have_row_based_replication= SHOW_OPTION_YES; -#else - have_row_based_replication= SHOW_OPTION_NO; -#endif #ifdef WITH_NDBCLUSTER_STORAGE_ENGINE have_ndbcluster=SHOW_OPTION_DISABLED; - global_system_variables.ndb_index_stat_enable=TRUE; + global_system_variables.ndb_index_stat_enable=FALSE; max_system_variables.ndb_index_stat_enable=TRUE; global_system_variables.ndb_index_stat_cache_entries=32; max_system_variables.ndb_index_stat_cache_entries=~0L; @@ -7065,9 +7257,9 @@ static void mysql_init_variables(void) have_ndbcluster=SHOW_OPTION_NO; #endif #ifdef HAVE_OPENSSL - have_openssl=SHOW_OPTION_YES; + have_ssl=SHOW_OPTION_YES; #else - have_openssl=SHOW_OPTION_NO; + have_ssl=SHOW_OPTION_NO; #endif #ifdef HAVE_BROKEN_REALPATH have_symlink=SHOW_OPTION_NO; @@ -7142,7 +7334,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), switch(optid) { case '#': #ifndef DBUG_OFF - DBUG_SET(argument ? argument : default_dbug_option); DBUG_SET_INITIAL(argument ? argument : default_dbug_option); #endif opt_endinfo=1; /* unireg: memory allocation */ @@ -7199,7 +7390,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), break; case 'T': test_flags= argument ? (uint) atoi(argument) : 0; - test_flags&= ~TEST_NO_THREADS; opt_endinfo=1; break; case (int) OPT_BIG_TABLES: @@ -7283,7 +7473,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), int id; if ((id= find_type(argument, &binlog_format_typelib, 2)) <= 0) { -#ifdef HAVE_ROW_BASED_REPLICATION fprintf(stderr, "Unknown binary log format: '%s' " "(should be one of '%s', '%s', '%s')\n", @@ -7291,14 +7480,9 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), binlog_format_names[BINLOG_FORMAT_STMT], binlog_format_names[BINLOG_FORMAT_ROW], binlog_format_names[BINLOG_FORMAT_MIXED]); -#else - fprintf(stderr, - "Unknown binary log format: '%s' (only legal value is '%s')\n", - argument, binlog_format_names[BINLOG_FORMAT_STMT]); -#endif exit(1); } - global_system_variables.binlog_format= id-1; + global_system_variables.binlog_format= opt_binlog_format_id= id - 1; break; } case (int)OPT_BINLOG_DO_DB: @@ -7437,11 +7621,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), opt_skip_show_db=1; opt_specialflag|=SPECIAL_SKIP_SHOW_DB; break; -#ifdef ONE_THREAD - case (int) OPT_ONE_THREAD: - test_flags |= TEST_NO_THREADS; -#endif - break; case (int) OPT_WANT_CORE: test_flags |= TEST_CORE_ON_SIGNAL; break; @@ -7482,6 +7661,29 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case (int) OPT_STANDALONE: /* Dummy option for NT */ break; #endif + /* + The following change issues a deprecation warning if the slave + configuration is specified either in the my.cnf file or on + the command-line. See BUG#21490. + */ + case OPT_MASTER_HOST: + case OPT_MASTER_USER: + case OPT_MASTER_PASSWORD: + case OPT_MASTER_PORT: + case OPT_MASTER_CONNECT_RETRY: + case OPT_MASTER_SSL: + case OPT_MASTER_SSL_KEY: + case OPT_MASTER_SSL_CERT: + case OPT_MASTER_SSL_CAPATH: + case OPT_MASTER_SSL_CIPHER: + case OPT_MASTER_SSL_CA: + if (!slave_warning_issued) //only show the warning once + { + slave_warning_issued = true; + WARN_DEPRECATED(NULL, "5.2", "for replication startup options", + "'CHANGE MASTER'"); + } + break; case OPT_CONSOLE: if (opt_console) opt_error_log= 0; // Force logs to stdout @@ -7536,10 +7738,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), break; } case OPT_MERGE: - if (opt_merge) - have_merge_db= SHOW_OPTION_YES; - else - have_merge_db= SHOW_OPTION_DISABLED; case OPT_BDB: break; case OPT_NDBCLUSTER: @@ -7691,6 +7889,23 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), sql_mode); break; } + case OPT_ONE_THREAD: + global_system_variables.thread_handling= 2; + break; + case OPT_THREAD_HANDLING: + { + if ((global_system_variables.thread_handling= + find_type(argument, &thread_handling_typelib, 2)) <= 0 || + (global_system_variables.thread_handling == SCHEDULER_POOL_OF_THREADS + && !HAVE_POOL_OF_THREADS)) + { + /* purecov: begin tested */ + fprintf(stderr,"Unknown/unsupported thread-handling: %s\n",argument); + exit(1); + /* purecov: end */ + } + break; + } case OPT_FT_BOOLEAN_SYNTAX: if (ft_boolean_check_syntax_string((byte*) argument)) { @@ -7771,10 +7986,6 @@ static void get_options(int argc,char **argv) if (opt_innodb) sql_print_warning("this binary does not contain INNODB storage engine"); #endif -#ifndef WITH_ISAM_STORAGE_ENGINE - if (opt_isam) - sql_print_warning("this binary does not contain ISAM storage engine"); -#endif if ((opt_log_slow_admin_statements || opt_log_queries_not_using_indexes) && !opt_slow_log) sql_print_warning("options --log-slow-admin-statements and --log-queries-not-using-indexes have no effect if --log-slow-queries is not set"); @@ -7815,6 +8026,7 @@ static void get_options(int argc,char **argv) if (mysqld_chroot) set_root(mysqld_chroot); #else + global_system_variables.thread_handling = SCHEDULER_NO_THREADS; max_allowed_packet= global_system_variables.max_allowed_packet; net_buffer_length= global_system_variables.net_buffer_length; #endif @@ -7845,6 +8057,17 @@ static void get_options(int argc,char **argv) &global_system_variables.datetime_format)) exit(1); +#ifdef EMBEDDED_LIBRARY + one_thread_scheduler(&thread_scheduler); +#else + if (global_system_variables.thread_handling <= + SCHEDULER_ONE_THREAD_PER_CONNECTION) + one_thread_per_connection_scheduler(&thread_scheduler); + else if (global_system_variables.thread_handling == SCHEDULER_NO_THREADS) + one_thread_scheduler(&thread_scheduler); + else + pool_of_threads_scheduler(&thread_scheduler); /* purecov: tested */ +#endif } @@ -7956,6 +8179,16 @@ static void fix_paths(void) exit(1); } #endif /* HAVE_REPLICATION */ + /* + Convert the secure-file-priv option to system format, allowing + a quick strcmp to check if read or write is in an allowed dir + */ + if (opt_secure_file_priv) + { + convert_dirname(buff, opt_secure_file_priv, NullS); + my_free(opt_secure_file_priv, MYF(0)); + opt_secure_file_priv= my_strdup(buff, MYF(MY_FAE)); + } } @@ -8062,6 +8295,8 @@ static int test_if_case_insensitive(const char *dir_name) /* Create file to store pid number */ +#ifndef EMBEDDED_LIBRARY + static void create_pid_file() { File file; @@ -8081,23 +8316,27 @@ static void create_pid_file() sql_perror("Can't start server: can't create PID file"); exit(1); } - +#endif /* EMBEDDED_LIBRARY */ /* Clear most status variables */ void refresh_status(THD *thd) { pthread_mutex_lock(&LOCK_status); - /* We must update the global status before cleaning up the thread */ + /* Add thread's status variabes to global status */ add_to_status(&global_status_var, &thd->status_var); + + /* Reset thread's status variables */ bzero((char*) &thd->status_var, sizeof(thd->status_var)); + /* Reset some global variables */ for (SHOW_VAR *ptr= status_vars; ptr->name; ptr++) { /* Note that SHOW_LONG_NOFLUSH variables are not reset */ if (ptr->type == SHOW_LONG) *(ulong*) ptr->value= 0; } + /* Reset the counters of all key caches (default and named). */ process_key_caches(reset_key_cache_counters); pthread_mutex_unlock(&LOCK_status); @@ -8119,23 +8358,12 @@ void refresh_status(THD *thd) *****************************************************************************/ #undef have_innodb #undef have_ndbcluster -#undef have_example_db -#undef have_archive_db #undef have_csv_db -#undef have_federated_db -#undef have_partition_db -#undef have_blackhole_db -#undef have_merge_db SHOW_COMP_OPTION have_innodb= SHOW_OPTION_NO; SHOW_COMP_OPTION have_ndbcluster= SHOW_OPTION_NO; -SHOW_COMP_OPTION have_example_db= SHOW_OPTION_NO; -SHOW_COMP_OPTION have_archive_db= SHOW_OPTION_NO; SHOW_COMP_OPTION have_csv_db= SHOW_OPTION_NO; -SHOW_COMP_OPTION have_federated_db= SHOW_OPTION_NO; SHOW_COMP_OPTION have_partition_db= SHOW_OPTION_NO; -SHOW_COMP_OPTION have_blackhole_db= SHOW_OPTION_NO; -SHOW_COMP_OPTION have_merge_db= SHOW_OPTION_NO; #ifndef WITH_INNOBASE_STORAGE_ENGINE uint innobase_flush_log_at_trx_commit; @@ -8145,7 +8373,6 @@ longlong innobase_log_file_size; long innobase_log_buffer_size; longlong innobase_buffer_pool_size; long innobase_additional_mem_pool_size; -long innobase_buffer_pool_awe_mem_mb; long innobase_file_io_threads, innobase_lock_wait_timeout; long innobase_force_recovery; long innobase_open_files; @@ -8156,7 +8383,9 @@ my_bool innobase_log_archive, innobase_use_doublewrite, innobase_use_checksums, innobase_file_per_table, - innobase_locks_unsafe_for_binlog; + innobase_locks_unsafe_for_binlog, + innobase_rollback_on_timeout, + innobase_stats_on_metadata; extern "C" { ulong srv_max_buf_pool_modified_pct; @@ -8190,5 +8419,3 @@ template class I_List<NAMED_LIST>; template class I_List<Statement>; template class I_List_iterator<Statement>; #endif - - diff --git a/sql/mysqld_suffix.h b/sql/mysqld_suffix.h index 405c5d855b7..b348f272db1 100644 --- a/sql/mysqld_suffix.h +++ b/sql/mysqld_suffix.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/net_serv.cc b/sql/net_serv.cc index a55fef5555c..2156888b8cf 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -48,9 +47,6 @@ #include <violite.h> #include <signal.h> #include <errno.h> -#ifdef __WIN__ -#include <winsock.h> -#endif #ifdef __NETWARE__ #include <sys/select.h> #endif @@ -221,6 +217,8 @@ my_bool net_realloc(NET *net, ulong length) -1 Don't know if data is ready or not */ +#if !defined(EMBEDDED_LIBRARY) + static int net_data_is_ready(my_socket sd) { #ifdef HAVE_POLL @@ -255,9 +253,10 @@ static int net_data_is_ready(my_socket sd) return 0; else return test(res ? FD_ISSET(sd, &sfds) : 0); -#endif +#endif /* HAVE_POLL */ } +#endif /* EMBEDDED_LIBRARY */ /* Remove unwanted characters from connection @@ -266,6 +265,7 @@ static int net_data_is_ready(my_socket sd) SYNOPSIS net_clear() net NET handler + clear_buffer If <> 0, then clear all data from communication buffer DESCRIPTION Read from socket until there is nothing more to read. Discard @@ -280,48 +280,54 @@ static int net_data_is_ready(my_socket sd) */ -void net_clear(NET *net) +void net_clear(NET *net, my_bool clear_buffer) { +#if !defined(EMBEDDED_LIBRARY) int count, ready; +#endif DBUG_ENTER("net_clear"); + #if !defined(EMBEDDED_LIBRARY) - while((ready= net_data_is_ready(net->vio->sd)) > 0) + if (clear_buffer) { - /* The socket is ready */ - if ((count= vio_read(net->vio, (char*) (net->buff), - (uint32) net->max_packet)) > 0) + while ((ready= net_data_is_ready(net->vio->sd)) > 0) { - DBUG_PRINT("info",("skipped %d bytes from file: %s", - count, vio_description(net->vio))); -#ifdef EXTRA_DEBUG - fprintf(stderr,"skipped %d bytes from file: %s\n", - count, vio_description(net->vio)); + /* The socket is ready */ + if ((count= vio_read(net->vio, (char*) (net->buff), + (uint32) net->max_packet)) > 0) + { + DBUG_PRINT("info",("skipped %d bytes from file: %s", + count, vio_description(net->vio))); +#if defined(EXTRA_DEBUG) && (MYSQL_VERSION_ID < 51000) + fprintf(stderr,"Error: net_clear() skipped %d bytes from file: %s\n", + count, vio_description(net->vio)); #endif + } + else + { + DBUG_PRINT("info",("socket ready but only EOF to read - disconnected")); + net->error= 2; + break; + } } - else - { - DBUG_PRINT("info",("socket ready but only EOF to read - disconnected")); - net->error= 2; - break; - } - } #ifdef NET_DATA_IS_READY_CAN_RETURN_MINUS_ONE - /* 'net_data_is_ready' returned "don't know" */ - if (ready == -1) - { - /* Read unblocking to clear net */ - my_bool old_mode; - if (!vio_blocking(net->vio, FALSE, &old_mode)) + /* 'net_data_is_ready' returned "don't know" */ + if (ready == -1) { - while ((count= vio_read(net->vio, (char*) (net->buff), - (uint32) net->max_packet)) > 0) - DBUG_PRINT("info",("skipped %d bytes from file: %s", - count, vio_description(net->vio))); - vio_blocking(net->vio, TRUE, &old_mode); + /* Read unblocking to clear net */ + my_bool old_mode; + if (!vio_blocking(net->vio, FALSE, &old_mode)) + { + while ((count= vio_read(net->vio, (char*) (net->buff), + (uint32) net->max_packet)) > 0) + DBUG_PRINT("info",("skipped %d bytes from file: %s", + count, vio_description(net->vio))); + vio_blocking(net->vio, TRUE, &old_mode); + } } +#endif /* NET_DATA_IS_READY_CAN_RETURN_MINUS_ONE */ } -#endif -#endif +#endif /* EMBEDDED_LIBRARY */ net->pkt_nr=net->compress_pkt_nr=0; /* Ready for new command */ net->write_pos=net->buff; DBUG_VOID_RETURN; @@ -596,14 +602,17 @@ net_real_write(NET *net,const char *packet,ulong len) } #endif /* HAVE_COMPRESS */ - /* DBUG_DUMP("net",packet,len); */ +#ifdef DEBUG_DATA_PACKETS + DBUG_DUMP("data",packet,len); +#endif + #ifndef NO_ALARM thr_alarm_init(&alarmed); if (net_blocking) thr_alarm(&alarmed,(uint) net->write_timeout,&alarm_buff); #else alarmed=0; - vio_timeout(net->vio, 1, net->write_timeout); + /* Write timeout is set in net_set_write_timeout */ #endif /* NO_ALARM */ pos=(char*) packet; end=pos+len; @@ -796,7 +805,7 @@ my_real_read(NET *net, ulong *complen) if (net_blocking) thr_alarm(&alarmed,net->read_timeout,&alarm_buff); #else - vio_timeout(net->vio, 0, net->read_timeout); + /* Read timeout is set in net_set_read_timeout */ #endif /* NO_ALARM */ pos = net->buff + net->where_b; /* net->packet -4 */ @@ -809,7 +818,7 @@ my_real_read(NET *net, ulong *complen) { my_bool interrupted = vio_should_retry(net->vio); - DBUG_PRINT("info",("vio_read returned %d, errno: %d", + DBUG_PRINT("info",("vio_read returned %ld, errno: %d", length, vio_errno(net->vio))); #if !defined(__WIN__) || defined(MYSQL_SERVER) /* @@ -894,7 +903,7 @@ my_real_read(NET *net, ulong *complen) (int) net->buff[net->where_b + 3], net->pkt_nr)); #ifdef EXTRA_DEBUG - fprintf(stderr,"Packets out of order (Found: %d, expected %d)\n", + fprintf(stderr,"Error: Packets out of order (Found: %d, expected %d)\n", (int) net->buff[net->where_b + 3], (uint) (uchar) net->pkt_nr); #endif @@ -1107,3 +1116,26 @@ my_net_read(NET *net) return len; } + +void net_set_read_timeout(NET *net, uint timeout) +{ + DBUG_ENTER("net_set_read_timeout"); + DBUG_PRINT("enter", ("timeout: %d", timeout)); + net->read_timeout= timeout; +#ifdef NO_ALARM + vio_timeout(net->vio, 0, timeout); +#endif + DBUG_VOID_RETURN; +} + + +void net_set_write_timeout(NET *net, uint timeout) +{ + DBUG_ENTER("net_set_write_timeout"); + DBUG_PRINT("enter", ("timeout: %d", timeout)); + net->write_timeout= timeout; +#ifdef NO_ALARM + vio_timeout(net->vio, 1, timeout); +#endif + DBUG_VOID_RETURN; +} diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 5878771c096..05b22cee935 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -220,6 +219,8 @@ public: } inline void merge_flags(SEL_ARG *arg) { maybe_flag|=arg->maybe_flag; } inline void maybe_smaller() { maybe_flag=1; } + /* Return true iff it's a single-point null interval */ + inline bool is_null_interval() { return maybe_null && max_value[0] == 1; } inline int cmp_min_to_min(SEL_ARG* arg) { return sel_cmp(field,min_value, arg->min_value, min_flag, arg->min_flag); @@ -540,7 +541,8 @@ class PARAM : public RANGE_OPT_PARAM { public: KEY_PART *key[MAX_KEY]; /* First key parts of keys used in the query */ - uint baseflag, max_key_part, range_count; + longlong baseflag; + uint max_key_part, range_count; char min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH], @@ -560,6 +562,7 @@ public: bool is_ror_scan; /* Number of ranges in the last checked tree->key */ uint n_ranges; + uint8 first_null_comp; /* first null component if any, 0 - otherwise */ }; class TABLE_READ_PLAN; @@ -606,9 +609,6 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge, double read_time); static TRP_GROUP_MIN_MAX *get_best_group_min_max(PARAM *param, SEL_TREE *tree); -static int get_index_merge_params(PARAM *param, key_map& needed_reg, - SEL_IMERGE *imerge, double *read_time, - ha_rows* imerge_rows); static double get_index_only_read_time(const PARAM* param, ha_rows records, int keynr); @@ -618,7 +618,6 @@ static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map, static void print_ror_scans_arr(TABLE *table, const char *msg, struct st_ror_scan_info **start, struct st_ror_scan_info **end); -static void print_rowid(byte* val, int len); static void print_quick(QUICK_SELECT_I *quick, const key_map *needed_reg); #endif @@ -930,7 +929,7 @@ QUICK_SELECT_I::QUICK_SELECT_I() QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr, bool no_alloc, MEM_ROOT *parent_alloc) - :dont_free(0),error(0),free_file(0),in_range(0),cur_range(NULL),range(0) + :dont_free(0),error(0),free_file(0),in_range(0),cur_range(NULL),last_range(0) { my_bitmap_map *bitmap; DBUG_ENTER("QUICK_RANGE_SELECT::QUICK_RANGE_SELECT"); @@ -1001,18 +1000,19 @@ QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT() if (file) { range_end(); + if (head->key_read) + { + head->key_read= 0; + file->extra(HA_EXTRA_NO_KEYREAD); + } if (free_file) { - DBUG_PRINT("info", ("Freeing separate handler %p (free=%d)", file, + DBUG_PRINT("info", ("Freeing separate handler 0x%lx (free: %d)", (long) file, free_file)); file->ha_external_lock(current_thd, F_UNLCK); file->close(); delete file; } - else - { - file->extra(HA_EXTRA_NO_KEYREAD); - } } delete_dynamic(&ranges); /* ranges are allocated in alloc */ free_root(&alloc,MYF(0)); @@ -1142,7 +1142,6 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler) { handler *save_file= file, *org_file; THD *thd; - MY_BITMAP *bitmap; DBUG_ENTER("QUICK_RANGE_SELECT::init_ror_merged_scan"); in_ror_merged_scan= 1; @@ -1195,7 +1194,11 @@ end: org_file= head->file; head->file= file; /* We don't have to set 'head->keyread' here as the 'file' is unique */ - head->mark_columns_used_by_index(index); + if (!head->no_keyread) + { + head->key_read= 1; + head->mark_columns_used_by_index(index); + } head->prepare_for_position(); head->file= org_file; bitmap_copy(&column_bitmap, head->read_set); @@ -1386,13 +1389,12 @@ int QUICK_ROR_UNION_SELECT::queue_cmp(void *arg, byte *val1, byte *val2) int QUICK_ROR_UNION_SELECT::reset() { - QUICK_SELECT_I* quick; + QUICK_SELECT_I *quick; int error; DBUG_ENTER("QUICK_ROR_UNION_SELECT::reset"); have_prev_rowid= FALSE; if (!scans_inited) { - QUICK_SELECT_I *quick; List_iterator_fast<QUICK_SELECT_I> it(quick_selects); while ((quick= it++)) { @@ -2011,7 +2013,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, double scan_time; DBUG_ENTER("SQL_SELECT::test_quick_select"); DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu", - keys_to_use.to_ulonglong(), (ulong) prev_tables, + (ulong) keys_to_use.to_ulonglong(), (ulong) prev_tables, (ulong) const_tables)); DBUG_PRINT("info", ("records: %lu", (ulong) head->file->stats.records)); delete quick; @@ -2048,7 +2050,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, /* set up parameter that is passed to all functions */ param.thd= thd; - param.baseflag=head->file->ha_table_flags(); + param.baseflag= head->file->ha_table_flags(); param.prev_tables=prev_tables | const_tables; param.read_tables=read_tables; param.current_table= head->map; @@ -2101,6 +2103,8 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, key_parts->null_bit= key_part_info->null_bit; key_parts->image_type = (key_info->flags & HA_SPATIAL) ? Field::itMBR : Field::itRAW; + /* Only HA_PART_KEY_SEG is used */ + key_parts->flag= (uint8) key_part_info->key_part_flag; } param.real_keynr[param.keys++]=idx; } @@ -2419,8 +2423,6 @@ static int find_used_partitions_imerge(PART_PRUNE_PARAM *ppar, static int find_used_partitions_imerge_list(PART_PRUNE_PARAM *ppar, List<SEL_IMERGE> &merges); static void mark_all_partitions_as_used(partition_info *part_info); -static uint32 part_num_to_part_id_range(PART_PRUNE_PARAM* prune_par, - uint32 num); #ifndef DBUG_OFF static void print_partitioning_index(KEY_PART *parts, KEY_PART *parts_end); @@ -2507,7 +2509,6 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond) prune_param.key= prune_param.range_param.key_parts; SEL_TREE *tree; - SEL_ARG *arg; int res; tree= get_mm_tree(range_par, pprune_cond); @@ -3131,7 +3132,7 @@ static bool fields_ok_for_partition_index(Field **pfield) for (; (*pfield); pfield++) { enum_field_types ftype= (*pfield)->real_type(); - if (ftype == FIELD_TYPE_ENUM || ftype == FIELD_TYPE_GEOMETRY) + if (ftype == MYSQL_TYPE_ENUM || ftype == MYSQL_TYPE_GEOMETRY) return FALSE; } return TRUE; @@ -3222,20 +3223,25 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar) { key_part->key= 0; key_part->part= part; - key_part->length= (*field)->pack_length_in_rec(); + key_part->length= (uint16) (*field)->pack_length_in_rec(); /* psergey-todo: check yet again if this is correct for tricky field types, e.g. see "Fix a fatal error in decimal key handling" in open_binary_frm() */ - key_part->store_length= (*field)->pack_length(); + key_part->store_length= (uint16) (*field)->pack_length(); if ((*field)->real_maybe_null()) key_part->store_length+= HA_KEY_NULL_LENGTH; - if ((*field)->type() == FIELD_TYPE_BLOB || + if ((*field)->type() == MYSQL_TYPE_BLOB || (*field)->real_type() == MYSQL_TYPE_VARCHAR) key_part->store_length+= HA_KEY_BLOB_LENGTH; key_part->field= (*field); key_part->image_type = Field::itRAW; + /* + We set keypart flag to 0 here as the only HA_PART_KEY_SEG is checked + in the RangeAnalysisModule. + */ + key_part->flag= 0; /* We don't set key_parts->null_bit as it will not be used */ ppar->is_part_keypart[part]= !in_subpart_fields; @@ -3390,7 +3396,7 @@ double get_sweep_read_cost(const PARAM *param, ha_rows records) n_blocks * (1.0 - pow(1.0 - 1.0/n_blocks, rows2double(records))); if (busy_blocks < 1.0) busy_blocks= 1.0; - DBUG_PRINT("info",("sweep: nblocks=%g, busy_blocks=%g", n_blocks, + DBUG_PRINT("info",("sweep: nblocks: %g, busy_blocks: %g", n_blocks, busy_blocks)); /* Disabled: Bail out if # of blocks to read is bigger than # of blocks in @@ -3414,7 +3420,7 @@ double get_sweep_read_cost(const PARAM *param, ha_rows records) result= busy_blocks; } } - DBUG_PRINT("info",("returning cost=%g", result)); + DBUG_PRINT("return",("cost: %g", result)); DBUG_RETURN(result); } @@ -3508,7 +3514,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge, ha_rows roru_total_records; double roru_intersect_part= 1.0; DBUG_ENTER("get_best_disjunct_quick"); - DBUG_PRINT("info", ("Full table scan cost =%g", read_time)); + DBUG_PRINT("info", ("Full table scan cost: %g", read_time)); if (!(range_scans= (TRP_RANGE**)alloc_root(param->mem_root, sizeof(TRP_RANGE*)* @@ -3552,7 +3558,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge, non_cpk_scan_records += (*cur_child)->records; } - DBUG_PRINT("info", ("index_merge scans cost=%g", imerge_cost)); + DBUG_PRINT("info", ("index_merge scans cost %g", imerge_cost)); if (imerge_too_expensive || (imerge_cost > read_time) || (non_cpk_scan_records+cpk_scan_records >= param->table->file->stats.records) && read_time != DBL_MAX) @@ -4166,7 +4172,7 @@ static bool ror_intersect_add(ROR_INTERSECT_INFO *info, DBUG_PRINT("info", ("Current out_rows= %g", info->out_rows)); DBUG_PRINT("info", ("Adding scan on %s", info->param->table->key_info[ror_scan->keynr].name)); - DBUG_PRINT("info", ("is_cpk_scan=%d",is_cpk_scan)); + DBUG_PRINT("info", ("is_cpk_scan: %d",is_cpk_scan)); selectivity_mult = ror_scan_selectivity(info, ror_scan); if (selectivity_mult == 1.0) @@ -4677,8 +4683,7 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, param->table->key_info[keynr].name, found_read_time, read_time)); - if (read_time > found_read_time && found_records != HA_POS_ERROR - /*|| read_time == DBL_MAX*/ ) + if (read_time > found_read_time && found_records != HA_POS_ERROR) { read_time= found_read_time; best_records= found_records; @@ -4918,8 +4923,8 @@ static SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Item_func *cond_func, type. Tree won't be built for values with different result types, so we check it here to avoid unnecessary work. */ - if (!func->array) - break; + if (!func->arg_types_compatible) + break; if (inv) { @@ -5006,7 +5011,8 @@ static SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Item_func *cond_func, for (uint idx= 0; idx < param->keys; idx++) { SEL_ARG *new_interval, *last_val; - if (((new_interval= tree2->keys[idx])) && + if (((new_interval= tree2->keys[idx])) && + (tree->keys[idx]) && ((last_val= tree->keys[idx]->last()))) { new_interval->min_value= last_val->max_value; @@ -5583,13 +5589,28 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, /* For comparison purposes allow invalid dates like 2000-01-32 */ orig_sql_mode= field->table->in_use->variables.sql_mode; if (value->real_item()->type() == Item::STRING_ITEM && - (field->type() == FIELD_TYPE_DATE || - field->type() == FIELD_TYPE_DATETIME)) + (field->type() == MYSQL_TYPE_DATE || + field->type() == MYSQL_TYPE_DATETIME)) field->table->in_use->variables.sql_mode|= MODE_INVALID_DATES; err= value->save_in_field_no_warnings(field, 1); if (err > 0 && field->cmp_type() != value->result_type()) { - tree= 0; + if ((type == Item_func::EQ_FUNC || type == Item_func::EQUAL_FUNC) && + value->result_type() == item_cmp_type(field->result_type(), + value->result_type())) + + { + tree= new (alloc) SEL_ARG(field, 0, 0); + tree->type= SEL_ARG::IMPOSSIBLE; + } + else + { + /* + TODO: We should return trees of the type SEL_ARG::IMPOSSIBLE + for the cases like int_field > 999999999999999999999999 as well. + */ + tree= 0; + } goto end; } if (err < 0) @@ -5655,7 +5676,9 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, } break; case Item_func::GT_FUNC: - if (field_is_equal_to_item(field,value)) + /* Don't use open ranges for partial key_segments */ + if (field_is_equal_to_item(field,value) && + !(key_part->flag & HA_PART_KEY_SEG)) tree->min_flag=NEAR_MIN; /* fall through */ case Item_func::GE_FUNC: @@ -7003,6 +7026,7 @@ check_quick_select(PARAM *param,uint idx,SEL_ARG *tree, bool update_tbl_stats) DBUG_ENTER("check_quick_select"); param->is_ror_scan= FALSE; + param->first_null_comp= 0; if (!tree) DBUG_RETURN(HA_POS_ERROR); // Can't use it @@ -7103,6 +7127,7 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree, ha_rows records=0, tmp; uint tmp_min_flag, tmp_max_flag, keynr, min_key_length, max_key_length; char *tmp_min_key, *tmp_max_key; + uint8 save_first_null_comp= param->first_null_comp; param->max_key_part=max(param->max_key_part,key_tree->part); if (key_tree->left != &null_element) @@ -7140,6 +7165,9 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree, param->is_ror_scan= FALSE; } + if (!param->first_null_comp && key_tree->is_null_interval()) + param->first_null_comp= key_tree->part+1; + if (key_tree->next_key_part && key_tree->next_key_part->part == key_tree->part+1 && key_tree->next_key_part->type == SEL_ARG::KEY_RANGE) @@ -7183,7 +7211,8 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree, (param->table->key_info[keynr].flags & (HA_NOSAME | HA_END_SPACE_KEY)) == HA_NOSAME && min_key_length == max_key_length && - !memcmp(param->min_key,param->max_key,min_key_length)) + !memcmp(param->min_key,param->max_key,min_key_length) && + !param->first_null_comp) { tmp=1; // Max one record param->n_ranges++; @@ -7258,6 +7287,7 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree, return tmp; records+=tmp; } + param->first_null_comp= save_first_null_comp; return records; } @@ -7644,6 +7674,7 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, key_part->length= key_info->key_part[part].length; key_part->store_length= key_info->key_part[part].store_length; key_part->null_bit= key_info->key_part[part].null_bit; + key_part->flag= (uint8) key_info->key_part[part].key_part_flag; } if (insert_dynamic(&quick->ranges,(gptr)&range)) goto err; @@ -7974,7 +8005,7 @@ int QUICK_RANGE_SELECT::reset() byte *mrange_buff; DBUG_ENTER("QUICK_RANGE_SELECT::reset"); next=0; - range= NULL; + last_range= NULL; in_range= FALSE; cur_range= (QUICK_RANGE**) ranges.buffer; @@ -8108,23 +8139,23 @@ int QUICK_RANGE_SELECT::get_next() { start_key= &mrange_slot->start_key; end_key= &mrange_slot->end_key; - range= *(cur_range++); + last_range= *(cur_range++); - start_key->key= (const byte*) range->min_key; - start_key->length= range->min_length; - start_key->flag= ((range->flag & NEAR_MIN) ? HA_READ_AFTER_KEY : - (range->flag & EQ_RANGE) ? + start_key->key= (const byte*) last_range->min_key; + start_key->length= last_range->min_length; + start_key->flag= ((last_range->flag & NEAR_MIN) ? HA_READ_AFTER_KEY : + (last_range->flag & EQ_RANGE) ? HA_READ_KEY_EXACT : HA_READ_KEY_OR_NEXT); - end_key->key= (const byte*) range->max_key; - end_key->length= range->max_length; + end_key->key= (const byte*) last_range->max_key; + end_key->length= last_range->max_length; /* We use HA_READ_AFTER_KEY here because if we are reading on a key prefix. We want to find all keys with this prefix. */ - end_key->flag= (range->flag & NEAR_MAX ? HA_READ_BEFORE_KEY : + end_key->flag= (last_range->flag & NEAR_MAX ? HA_READ_BEFORE_KEY : HA_READ_AFTER_KEY); - mrange_slot->range_flag= range->flag; + mrange_slot->range_flag= last_range->flag; } result= file->read_multi_range_first(&mrange, multi_range, count, @@ -8180,7 +8211,7 @@ int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length, byte *cur_prefix) { int result; key_range start_key, end_key; - if (range) + if (last_range) { /* Read the next record in the same range with prefix after cur_prefix. */ DBUG_ASSERT(cur_prefix != 0); @@ -8194,35 +8225,35 @@ int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length, byte *cur_prefix) if (count == 0) { /* Ranges have already been used up before. None is left for read. */ - range= 0; + last_range= 0; DBUG_RETURN(HA_ERR_END_OF_FILE); } - range= *(cur_range++); + last_range= *(cur_range++); - start_key.key= (const byte*) range->min_key; - start_key.length= min(range->min_length, prefix_length); - start_key.flag= ((range->flag & NEAR_MIN) ? HA_READ_AFTER_KEY : - (range->flag & EQ_RANGE) ? + start_key.key= (const byte*) last_range->min_key; + start_key.length= min(last_range->min_length, prefix_length); + start_key.flag= ((last_range->flag & NEAR_MIN) ? HA_READ_AFTER_KEY : + (last_range->flag & EQ_RANGE) ? HA_READ_KEY_EXACT : HA_READ_KEY_OR_NEXT); - end_key.key= (const byte*) range->max_key; - end_key.length= min(range->max_length, prefix_length); + end_key.key= (const byte*) last_range->max_key; + end_key.length= min(last_range->max_length, prefix_length); /* We use READ_AFTER_KEY here because if we are reading on a key prefix we want to find all keys with this prefix */ - end_key.flag= (range->flag & NEAR_MAX ? HA_READ_BEFORE_KEY : + end_key.flag= (last_range->flag & NEAR_MAX ? HA_READ_BEFORE_KEY : HA_READ_AFTER_KEY); - result= file->read_range_first(range->min_length ? &start_key : 0, - range->max_length ? &end_key : 0, - test(range->flag & EQ_RANGE), + result= file->read_range_first(last_range->min_length ? &start_key : 0, + last_range->max_length ? &end_key : 0, + test(last_range->flag & EQ_RANGE), sorted); - if (range->flag == (UNIQUE_RANGE | EQ_RANGE)) - range=0; // Stop searching + if (last_range->flag == (UNIQUE_RANGE | EQ_RANGE)) + last_range= 0; // Stop searching if (result != HA_ERR_END_OF_FILE) DBUG_RETURN(result); - range=0; // No matching rows; go to next range + last_range= 0; // No matching rows; go to next range } } @@ -8236,11 +8267,11 @@ int QUICK_RANGE_SELECT_GEOM::get_next() for (;;) { int result; - if (range) + if (last_range) { // Already read through key - result= file->index_next_same(record, (byte*) range->min_key, - range->min_length); + result= file->index_next_same(record, (byte*) last_range->min_key, + last_range->min_length); if (result != HA_ERR_END_OF_FILE) DBUG_RETURN(result); } @@ -8249,18 +8280,18 @@ int QUICK_RANGE_SELECT_GEOM::get_next() if (count == 0) { /* Ranges have already been used up before. None is left for read. */ - range= 0; + last_range= 0; DBUG_RETURN(HA_ERR_END_OF_FILE); } - range= *(cur_range++); + last_range= *(cur_range++); result= file->index_read(record, - (byte*) range->min_key, - range->min_length, - (ha_rkey_function)(range->flag ^ GEOM_FLAG)); + (byte*) last_range->min_key, + last_range->min_length, + (ha_rkey_function)(last_range->flag ^ GEOM_FLAG)); if (result != HA_ERR_KEY_NOT_FOUND && result != HA_ERR_END_OF_FILE) DBUG_RETURN(result); - range=0; // Not found, to next range + last_range= 0; // Not found, to next range } } @@ -8285,7 +8316,7 @@ int QUICK_RANGE_SELECT_GEOM::get_next() bool QUICK_RANGE_SELECT::row_in_ranges() { - QUICK_RANGE *range; + QUICK_RANGE *res; uint min= 0; uint max= ranges.elements - 1; uint mid= (max + min)/2; @@ -8301,8 +8332,8 @@ bool QUICK_RANGE_SELECT::row_in_ranges() max= mid; mid= (min + max) / 2; } - range= *(QUICK_RANGE**)dynamic_array_ptr(&ranges, mid); - return (!cmp_next(range) && !cmp_prev(range)); + res= *(QUICK_RANGE**)dynamic_array_ptr(&ranges, mid); + return (!cmp_next(res) && !cmp_prev(res)); } /* @@ -8316,14 +8347,14 @@ bool QUICK_RANGE_SELECT::row_in_ranges() */ QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q, - uint used_key_parts) + uint used_key_parts_arg) :QUICK_RANGE_SELECT(*q), rev_it(rev_ranges) { QUICK_RANGE *r; QUICK_RANGE **pr= (QUICK_RANGE**)ranges.buffer; - QUICK_RANGE **last_range= pr + ranges.elements; - for (; pr!=last_range; pr++) + QUICK_RANGE **end_range= pr + ranges.elements; + for (; pr!=end_range; pr++) rev_ranges.push_front(*pr); /* Remove EQ_RANGE flag for keys that are not using the full key */ @@ -8357,11 +8388,11 @@ int QUICK_SELECT_DESC::get_next() for (;;) { int result; - if (range) + if (last_range) { // Already read through key - result = ((range->flag & EQ_RANGE) - ? file->index_next_same(record, (byte*) range->min_key, - range->min_length) : + result = ((last_range->flag & EQ_RANGE) + ? file->index_next_same(record, (byte*) last_range->min_key, + last_range->min_length) : file->index_prev(record)); if (!result) { @@ -8372,47 +8403,49 @@ int QUICK_SELECT_DESC::get_next() DBUG_RETURN(result); } - if (!(range=rev_it++)) + if (!(last_range= rev_it++)) DBUG_RETURN(HA_ERR_END_OF_FILE); // All ranges used - if (range->flag & NO_MAX_RANGE) // Read last record + if (last_range->flag & NO_MAX_RANGE) // Read last record { int local_error; if ((local_error=file->index_last(record))) DBUG_RETURN(local_error); // Empty table - if (cmp_prev(range) == 0) + if (cmp_prev(last_range) == 0) DBUG_RETURN(0); - range=0; // No matching records; go to next range + last_range= 0; // No match; go to next range continue; } - if (range->flag & EQ_RANGE) + if (last_range->flag & EQ_RANGE) { - result = file->index_read(record, (byte*) range->max_key, - range->max_length, HA_READ_KEY_EXACT); + result= file->index_read(record, (byte*) last_range->max_key, + last_range->max_length, HA_READ_KEY_EXACT); } else { - DBUG_ASSERT(range->flag & NEAR_MAX || range_reads_after_key(range)); - result=file->index_read(record, (byte*) range->max_key, - range->max_length, - ((range->flag & NEAR_MAX) ? - HA_READ_BEFORE_KEY : HA_READ_PREFIX_LAST_OR_PREV)); + DBUG_ASSERT(last_range->flag & NEAR_MAX || + range_reads_after_key(last_range)); + result=file->index_read(record, (byte*) last_range->max_key, + last_range->max_length, + ((last_range->flag & NEAR_MAX) ? + HA_READ_BEFORE_KEY : + HA_READ_PREFIX_LAST_OR_PREV)); } if (result) { if (result != HA_ERR_KEY_NOT_FOUND && result != HA_ERR_END_OF_FILE) DBUG_RETURN(result); - range=0; // Not found, to next range + last_range= 0; // Not found, to next range continue; } - if (cmp_prev(range) == 0) + if (cmp_prev(last_range) == 0) { - if (range->flag == (UNIQUE_RANGE | EQ_RANGE)) - range = 0; // Stop searching + if (last_range->flag == (UNIQUE_RANGE | EQ_RANGE)) + last_range= 0; // Stop searching DBUG_RETURN(0); // Found key is in range } - range = 0; // To next range + last_range= 0; // To next range } } @@ -8865,7 +8898,7 @@ static TRP_GROUP_MIN_MAX * get_best_group_min_max(PARAM *param, SEL_TREE *tree) { THD *thd= param->thd; - JOIN *join= thd->lex->select_lex.join; + JOIN *join= thd->lex->current_select->join; TABLE *table= param->table; bool have_min= FALSE; /* TRUE if there is a MIN function. */ bool have_max= FALSE; /* TRUE if there is a MAX function. */ @@ -8886,12 +8919,12 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) DBUG_ENTER("get_best_group_min_max"); /* Perform few 'cheap' tests whether this access method is applicable. */ - if (!join || (thd->lex->sql_command != SQLCOM_SELECT)) + if (!join) DBUG_RETURN(NULL); /* This is not a select statement. */ if ((join->tables != 1) || /* The query must reference one table. */ ((!join->group_list) && /* Neither GROUP BY nor a DISTINCT query. */ (!join->select_distinct)) || - (thd->lex->select_lex.olap == ROLLUP_TYPE)) /* Check (B3) for ROLLUP */ + (join->select_lex->olap == ROLLUP_TYPE)) /* Check (B3) for ROLLUP */ DBUG_RETURN(NULL); if (table->s->keys == 0) /* There are no indexes to use. */ DBUG_RETURN(NULL); @@ -9691,8 +9724,8 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts, *records= num_groups; DBUG_PRINT("info", - ("table rows=%u, keys/block=%u, keys/group=%u, result rows=%u, blocks=%u", - table_records, keys_per_block, keys_per_group, *records, + ("table rows: %u keys/block: %u keys/group: %u result rows: %lu blocks: %u", + table_records, keys_per_block, keys_per_group, (ulong) *records, num_blocks)); DBUG_VOID_RETURN; } @@ -9727,7 +9760,7 @@ TRP_GROUP_MIN_MAX::make_quick(PARAM *param, bool retrieve_full_rows, DBUG_ENTER("TRP_GROUP_MIN_MAX::make_quick"); quick= new QUICK_GROUP_MIN_MAX_SELECT(param->table, - param->thd->lex->select_lex.join, + param->thd->lex->current_select->join, have_min, have_max, min_max_arg_part, group_prefix_len, used_key_parts, index_info, index, read_cost, records, @@ -10140,14 +10173,13 @@ int QUICK_GROUP_MIN_MAX_SELECT::reset(void) DBUG_ENTER("QUICK_GROUP_MIN_MAX_SELECT::reset"); file->extra(HA_EXTRA_KEYREAD); /* We need only the key attributes */ - result= file->ha_index_init(index, 1); - result= file->index_last(record); - if (result == HA_ERR_END_OF_FILE) - DBUG_RETURN(0); - if (result) + if ((result= file->ha_index_init(index,1))) DBUG_RETURN(result); if (quick_prefix_select && quick_prefix_select->reset()) DBUG_RETURN(1); + result= file->index_last(record); + if (result == HA_ERR_END_OF_FILE) + DBUG_RETURN(0); /* Save the prefix of the last group. */ key_copy(last_prefix, record, index_info, group_prefix_len); @@ -10196,7 +10228,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::get_next() #else int result; #endif - int is_last_prefix; + int is_last_prefix= 0; DBUG_ENTER("QUICK_GROUP_MIN_MAX_SELECT::get_next"); @@ -10211,13 +10243,18 @@ int QUICK_GROUP_MIN_MAX_SELECT::get_next() Check if this is the last group prefix. Notice that at this point this->record contains the current prefix in record format. */ - is_last_prefix= key_cmp(index_info->key_part, last_prefix, - group_prefix_len); - DBUG_ASSERT(is_last_prefix <= 0); - if (result == HA_ERR_KEY_NOT_FOUND) - continue; - if (result) + if (!result) + { + is_last_prefix= key_cmp(index_info->key_part, last_prefix, + group_prefix_len); + DBUG_ASSERT(is_last_prefix <= 0); + } + else + { + if (result == HA_ERR_KEY_NOT_FOUND) + continue; break; + } if (have_min) { @@ -10805,7 +10842,7 @@ static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map, if (!tmp.length()) tmp.append(STRING_WITH_LEN("(empty)")); - DBUG_PRINT("info", ("SEL_TREE %p (%s) scans:%s", tree, msg, tmp.ptr())); + DBUG_PRINT("info", ("SEL_TREE: 0x%lx (%s) scans: %s", (long) tree, msg, tmp.ptr())); DBUG_VOID_RETURN; } @@ -10904,23 +10941,9 @@ static void print_quick(QUICK_SELECT_I *quick, const key_map *needed_reg) } -static void print_rowid(byte* val, int len) -{ - byte *pb; - DBUG_LOCK_FILE; - fputc('\"', DBUG_FILE); - for (pb= val; pb!= val + len; ++pb) - fprintf(DBUG_FILE, "%c", *pb); - fprintf(DBUG_FILE, "\", hex: "); - - for (pb= val; pb!= val + len; ++pb) - fprintf(DBUG_FILE, "%x ", *pb); - fputc('\n', DBUG_FILE); - DBUG_UNLOCK_FILE; -} - void QUICK_RANGE_SELECT::dbug_dump(int indent, bool verbose) { + /* purecov: begin inspected */ fprintf(DBUG_FILE, "%*squick range select, key %s, length: %d\n", indent, "", head->key_info[index].name, max_used_key_length); @@ -10928,8 +10951,8 @@ void QUICK_RANGE_SELECT::dbug_dump(int indent, bool verbose) { QUICK_RANGE *range; QUICK_RANGE **pr= (QUICK_RANGE**)ranges.buffer; - QUICK_RANGE **last_range= pr + ranges.elements; - for (; pr!=last_range; ++pr) + QUICK_RANGE **end_range= pr + ranges.elements; + for (; pr != end_range; ++pr) { fprintf(DBUG_FILE, "%*s", indent + 2, ""); range= *pr; @@ -10954,6 +10977,7 @@ void QUICK_RANGE_SELECT::dbug_dump(int indent, bool verbose) fputs("\n",DBUG_FILE); } } + /* purecov: end */ } void QUICK_INDEX_MERGE_SELECT::dbug_dump(int indent, bool verbose) @@ -11047,7 +11071,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::dbug_dump(int indent, bool verbose) #endif /* NOT_USED */ /***************************************************************************** -** Instantiate templates +** Instantiate templates *****************************************************************************/ #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION diff --git a/sql/opt_range.h b/sql/opt_range.h index a88c79e8bab..d82e1dc459e 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -27,6 +26,8 @@ typedef struct st_key_part { uint16 key,part, store_length, length; uint8 null_bit; + /* Keypart flags (0 if partition pruning is used) */ + uint8 flag; Field *field; Field::imagetype image_type; } KEY_PART; @@ -300,7 +301,7 @@ protected: DYNAMIC_ARRAY ranges; /* ordered array of range ptrs */ QUICK_RANGE **cur_range; /* current element in ranges */ - QUICK_RANGE *range; + QUICK_RANGE *last_range; KEY_PART *key_parts; KEY_PART_INFO *key_part_info; int cmp_next(QUICK_RANGE *range); diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 7d6305594fd..5bd5ec4b42d 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -98,9 +97,9 @@ static ulonglong get_exact_record_count(TABLE_LIST *tables) GROUP BY part. RETURN VALUES - 0 No errors - 1 if all items were resolved - -1 on impossible conditions + 0 no errors + 1 if all items were resolved + HA_ERR_KEY_NOT_FOUND on impossible conditions OR an error number from my_base.h HA_ERR_... if a deadlock or a lock wait timeout happens, for example */ @@ -167,7 +166,12 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) } else { - tl->table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); + error= tl->table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); + if(error) + { + tl->table->file->print_error(error, MYF(0)); + return error; + } count*= tl->table->file->stats.records; } } @@ -263,7 +267,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) if (error) { if (error == HA_ERR_KEY_NOT_FOUND || error == HA_ERR_END_OF_FILE) - return -1; // No rows matching WHERE + return HA_ERR_KEY_NOT_FOUND; // No rows matching WHERE /* HA_ERR_LOCK_DEADLOCK or some other error */ table->file->print_error(error, MYF(0)); return(error); @@ -350,7 +354,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) if (error) { if (error == HA_ERR_KEY_NOT_FOUND || error == HA_ERR_END_OF_FILE) - return -1; // No rows matching WHERE + return HA_ERR_KEY_NOT_FOUND; // No rows matching WHERE /* HA_ERR_LOCK_DEADLOCK or some other error */ table->file->print_error(error, MYF(0)); return(error); diff --git a/sql/parse_file.cc b/sql/parse_file.cc index 0a2d4012af4..f5b62e3afe2 100644 --- a/sql/parse_file.cc +++ b/sql/parse_file.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -229,7 +228,7 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name, if (dir) { - fn_format(path, file_name->str, dir->str, 0, MY_UNPACK_FILENAME); + fn_format(path, file_name->str, dir->str, "", MY_UNPACK_FILENAME); path_end= strlen(path); } else diff --git a/sql/parse_file.h b/sql/parse_file.h index 33871588e11..21873b32904 100644 --- a/sql/parse_file.h +++ b/sql/parse_file.h @@ -3,8 +3,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -106,5 +105,4 @@ public: MEM_ROOT *mem_root, bool bad_format_errors); }; - #endif /* _PARSE_FILE_H_ */ diff --git a/sql/partition_element.h b/sql/partition_element.h index 1e2769bc21a..c056d40b85b 100644 --- a/sql/partition_element.h +++ b/sql/partition_element.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000,200666666 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -12,7 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** * An enum and a struct to handle partitioning and subpartitioning. diff --git a/sql/partition_info.cc b/sql/partition_info.cc index a20fca9404b..a7f9bd413c6 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -248,7 +247,6 @@ bool partition_info::set_up_default_subpartitions(handler *file, HA_CREATE_INFO *info) { uint i, j; - char *default_name, *name_ptr; bool result= TRUE; partition_element *part_elem; List_iterator<partition_element> part_it(partitions); @@ -485,6 +483,8 @@ bool partition_info::check_range_constants() DBUG_ENTER("partition_info::check_range_constants"); DBUG_PRINT("enter", ("INT_RESULT with %d parts", no_parts)); + LINT_INIT(current_largest); + part_result_type= INT_RESULT; range_int_array= (longlong*)sql_alloc(no_parts * sizeof(longlong)); if (unlikely(range_int_array == NULL)) @@ -662,7 +662,8 @@ bool partition_info::check_list_constants() qsort((void*)list_array, no_list_values, sizeof(LIST_PART_ENTRY), &list_part_cmp); - i= prev_value= 0; //prev_value initialised to quiet compiler + i= 0; + LINT_INIT(prev_value); do { DBUG_ASSERT(i < no_list_values); @@ -877,7 +878,6 @@ void partition_info::print_no_partition_found(TABLE *table) bool partition_info::set_up_charset_field_preps() { Field *field, **ptr; - char *field_buf; char **char_ptrs; unsigned i; bool found; @@ -919,7 +919,7 @@ bool partition_info::set_up_charset_field_preps() { if (field_is_partition_charset(field)) { - CHARSET_INFO *cs= ((Field_str*)field)->charset(); + char *field_buf; size= field->pack_length(); if (!(field_buf= sql_calloc(size))) goto error; @@ -954,8 +954,9 @@ bool partition_info::set_up_charset_field_preps() while ((field= *(ptr++))) { unsigned j= 0; - Field *part_field; CHARSET_INFO *cs; + char *field_buf; + LINT_INIT(field_buf); if (!field_is_partition_charset(field)) continue; @@ -981,7 +982,6 @@ bool partition_info::set_up_charset_field_preps() } if (tot_fields) { - Field *part_field, *subpart_field; uint j,k,l; size= tot_fields*sizeof(char**); diff --git a/sql/partition_info.h b/sql/partition_info.h index 09d827d44c4..8bcc769054f 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000,2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -12,7 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef USE_PRAGMA_INTERFACE #pragma interface /* gcc class implementation */ diff --git a/sql/password.c b/sql/password.c index c6d904bc45b..1ff67888ea4 100644 --- a/sql/password.c +++ b/sql/password.c @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -473,7 +472,7 @@ scramble(char *to, const char *message, const char *password) */ my_bool -check_scramble(const char *scramble, const char *message, +check_scramble(const char *scramble_arg, const char *message, const uint8 *hash_stage2) { SHA1_CONTEXT sha1_context; @@ -486,7 +485,7 @@ check_scramble(const char *scramble, const char *message, mysql_sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE); mysql_sha1_result(&sha1_context, buf); /* encrypt scramble */ - my_crypt((char *) buf, buf, (const uchar *) scramble, SCRAMBLE_LENGTH); + my_crypt((char *) buf, buf, (const uchar *) scramble_arg, SCRAMBLE_LENGTH); /* now buf supposedly contains hash_stage1: so we can get hash_stage2 */ mysql_sha1_reset(&sha1_context); mysql_sha1_input(&sha1_context, buf, SHA1_HASH_SIZE); @@ -496,7 +495,8 @@ check_scramble(const char *scramble, const char *message, /* - Convert scrambled password from asciiz hex string to binary form. + Convert scrambled password from asciiz hex string to binary form. + SYNOPSIS get_salt_from_password() res OUT buf to hold password. Must be at least SHA1_HASH_SIZE diff --git a/sql/procedure.cc b/sql/procedure.cc index 554e2cd0565..bbfabc46608 100644 --- a/sql/procedure.cc +++ b/sql/procedure.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2002, 2004-2005 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/procedure.h b/sql/procedure.h index 5c8a3387eec..6a731766046 100644 --- a/sql/procedure.h +++ b/sql/procedure.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2005 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/protocol.cc b/sql/protocol.cc index e00a70cf3a2..05e98c68e4e 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -28,8 +27,10 @@ #include <stdarg.h> static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024; -static void write_eof_packet(THD *thd, NET *net); void net_send_error_packet(THD *thd, uint sql_errno, const char *err); +#ifndef EMBEDDED_LIBRARY +static void write_eof_packet(THD *thd, NET *net); +#endif #ifndef EMBEDDED_LIBRARY bool Protocol::net_store_data(const char *from, uint length) @@ -46,7 +47,7 @@ bool Protocol_prep::net_store_data(const char *from, uint length) packet->realloc(packet_length+9+length)) return 1; char *to=(char*) net_store_length((char*) packet->ptr()+packet_length, - (ulonglong) length); + length); memcpy(to,from,length); packet->length((uint) (to+length-packet->ptr())); return 0; @@ -79,6 +80,7 @@ void net_send_error(THD *thd, uint sql_errno, const char *err) if (net && net->no_send_error) { thd->clear_error(); + thd->is_fatal_error= 0; // Error message is given DBUG_PRINT("info", ("sending error messages prohibited")); DBUG_VOID_RETURN; } @@ -111,7 +113,7 @@ void net_send_error(THD *thd, uint sql_errno, const char *err) net_send_error_packet(thd, sql_errno, err); - thd->is_fatal_error=0; // Error message is given + thd->is_fatal_error= 0; // Error message is given thd->net.report_error= 0; /* Abort multi-result sets */ @@ -156,6 +158,7 @@ net_printf_error(THD *thd, uint errcode, ...) if (net && net->no_send_error) { thd->clear_error(); + thd->is_fatal_error= 0; // Error message is given DBUG_PRINT("info", ("sending error messages prohibited")); DBUG_VOID_RETURN; } @@ -280,8 +283,8 @@ send_ok(THD *thd, ha_rows affected_rows, ulonglong id, const char *message) } buff[0]=0; // No fields - pos=net_store_length(buff+1,(ulonglong) affected_rows); - pos=net_store_length(pos, (ulonglong) id); + pos=net_store_length(buff+1,affected_rows); + pos=net_store_length(pos, id); if (thd->client_capabilities & CLIENT_PROTOCOL_41) { DBUG_PRINT("info", @@ -456,7 +459,7 @@ void net_send_error_packet(THD *thd, uint sql_errno, const char *err) ulonglong for bigger numbers. */ -char *net_store_length(char *pkg, uint length) +static char *net_store_length_fast(char *pkg, uint length) { uchar *packet=(uchar*) pkg; if (length < 251) @@ -479,7 +482,7 @@ char *net_store_length(char *pkg, uint length) char *net_store_data(char *to,const char *from, uint length) { - to=net_store_length(to,length); + to=net_store_length_fast(to,length); memcpy(to,from,length); return to+length; } @@ -488,7 +491,7 @@ char *net_store_data(char *to,int32 from) { char buff[20]; uint length=(uint) (int10_to_str(from,buff,10)-buff); - to=net_store_length(to,length); + to=net_store_length_fast(to,length); memcpy(to,buff,length); return to+length; } @@ -497,7 +500,7 @@ char *net_store_data(char *to,longlong from) { char buff[22]; uint length=(uint) (longlong10_to_str(from,buff,10)-buff); - to=net_store_length(to,length); + to=net_store_length_fast(to,length); memcpy(to,buff,length); return to+length; } @@ -561,7 +564,7 @@ bool Protocol::send_fields(List<Item> *list, uint flags) if (flags & SEND_NUM_ROWS) { // Packet with number of elements - char *pos=net_store_length(buff, (uint) list->elements); + char *pos=net_store_length(buff, list->elements); (void) my_net_write(&thd->net, buff,(uint) (pos-buff)); } @@ -934,15 +937,15 @@ bool Protocol_simple::store(Field *field) char buff[MAX_FIELD_WIDTH]; String str(buff,sizeof(buff), &my_charset_bin); CHARSET_INFO *tocs= this->thd->variables.character_set_results; +#ifndef DBUG_OFF TABLE *table= field->table; -#ifdef DBUG_OFF my_bitmap_map *old_map= 0; if (table->file) old_map= dbug_tmp_use_all_columns(table, table->read_set); #endif field->val_str(&str); -#ifdef DBUG_OFF +#ifndef DBUG_OFF if (old_map) dbug_tmp_restore_column_map(table->read_set, old_map); #endif diff --git a/sql/protocol.h b/sql/protocol.h index 85c22724b74..6c4c7414ea5 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2002-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -59,6 +58,8 @@ public: String *storage_packet() { return packet; } inline void free() { packet->free(); } virtual bool write(); + inline bool store(int from) + { return store_long((longlong) from); } inline bool store(uint32 from) { return store_long((longlong) from); } inline bool store(longlong from) @@ -166,7 +167,6 @@ void send_ok(THD *thd, ha_rows affected_rows=0L, ulonglong id=0L, const char *info=0); void send_eof(THD *thd); bool send_old_password_request(THD *thd); -char *net_store_length(char *packet,uint length); char *net_store_data(char *to,const char *from, uint length); char *net_store_data(char *to,int32 from); char *net_store_data(char *to,longlong from); diff --git a/sql/records.cc b/sql/records.cc index b2505600b22..0923ab1d75e 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -20,7 +19,7 @@ #include "mysql_priv.h" static int rr_quick(READ_RECORD *info); -static int rr_sequential(READ_RECORD *info); +int rr_sequential(READ_RECORD *info); static int rr_from_tempfile(READ_RECORD *info); static int rr_unpack_from_tempfile(READ_RECORD *info); static int rr_unpack_from_buffer(READ_RECORD *info); @@ -251,6 +250,7 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, } /* init_read_record */ + void end_read_record(READ_RECORD *info) { /* free cache if used */ if (info->cache) @@ -260,7 +260,7 @@ void end_read_record(READ_RECORD *info) } if (info->table) { - filesort_free_buffers(info->table); + filesort_free_buffers(info->table,0); (void) info->file->extra(HA_EXTRA_NO_CACHE); if (info->read_record != rr_quick) // otherwise quick_range does it (void) info->file->ha_index_or_rnd_end(); @@ -356,7 +356,7 @@ static int rr_index(READ_RECORD *info) } -static int rr_sequential(READ_RECORD *info) +int rr_sequential(READ_RECORD *info) { int tmp; while ((tmp=info->file->rnd_next(info->record))) diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 2b034d50d6a..934a6821514 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB & Sasha +/* Copyright (C) 2001-2006 MySQL AB & Sasha 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -58,6 +57,7 @@ static Slave_log_event* find_slave_event(IO_CACHE* log, functions like register_slave()) are working. */ +#if NOT_USED static int init_failsafe_rpl_thread(THD* thd) { DBUG_ENTER("init_failsafe_rpl_thread"); @@ -73,23 +73,19 @@ static int init_failsafe_rpl_thread(THD* thd) thd->net.read_timeout = slave_net_timeout; thd->max_client_packet_length=thd->net.max_packet; pthread_mutex_lock(&LOCK_thread_count); - thd->thread_id = thread_id++; + thd->thread_id= thd->variables.pseudo_thread_id= thread_id++; pthread_mutex_unlock(&LOCK_thread_count); if (init_thr_lock() || thd->store_globals()) { + /* purecov: begin inspected */ close_connection(thd, ER_OUT_OF_RESOURCES, 1); // is this needed? statistic_increment(aborted_connects,&LOCK_status); - end_thread(thd,0); + one_thread_per_connection_end(thd,0); DBUG_RETURN(-1); + /* purecov: end */ } -#if !defined(__WIN__) && !defined(__NETWARE__) - sigset_t set; - VOID(sigemptyset(&set)); // Get mask in use - VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); -#endif - thd->mem_root->free= thd->mem_root->used= 0; if (thd->variables.max_join_size == HA_POS_ERROR) thd->options|= OPTION_BIG_SELECTS; @@ -99,7 +95,7 @@ static int init_failsafe_rpl_thread(THD* thd) thd->set_time(); DBUG_RETURN(0); } - +#endif void change_rpl_status(RPL_STATUS from_status, RPL_STATUS to_status) { @@ -530,11 +526,11 @@ HOSTS"; while ((row= mysql_fetch_row(res))) { - uint32 server_id; + uint32 log_server_id; SLAVE_INFO* si, *old_si; - server_id = atoi(row[0]); + log_server_id = atoi(row[0]); if ((old_si= (SLAVE_INFO*)hash_search(&slave_list, - (byte*)&server_id,4))) + (byte*)&log_server_id,4))) si = old_si; else { @@ -544,7 +540,7 @@ HOSTS"; pthread_mutex_unlock(&LOCK_slave_list); goto err; } - si->server_id = server_id; + si->server_id = log_server_id; my_hash_insert(&slave_list, (byte*)si); } strmake(si->host, row[1], sizeof(si->host)-1); @@ -564,8 +560,8 @@ err: mysql_free_result(res); if (error) { - sql_print_error("While trying to obtain the list of slaves from the master \ -'%s:%d', user '%s' got the following error: '%s'", + sql_print_error("While trying to obtain the list of slaves from the master " + "'%s:%d', user '%s' got the following error: '%s'", mi->host, mi->port, mi->user, error); DBUG_RETURN(1); } @@ -573,12 +569,14 @@ err: } +#if NOT_USED int find_recovery_captain(THD* thd, MYSQL* mysql) { return 0; } +#endif - +#if NOT_USED pthread_handler_t handle_failsafe_rpl(void *arg) { DBUG_ENTER("handle_failsafe_rpl"); @@ -626,7 +624,7 @@ err: pthread_exit(0); DBUG_RETURN(0); } - +#endif bool show_slave_hosts(THD* thd) { @@ -916,14 +914,14 @@ bool load_master_data(THD* thd) setting active_mi, because init_master_info() sets active_mi with defaults. */ - int error; + int error_2; if (init_master_info(active_mi, master_info_file, relay_log_info_file, 0, (SLAVE_IO | SLAVE_SQL))) my_message(ER_MASTER_INFO, ER(ER_MASTER_INFO), MYF(0)); strmake(active_mi->master_log_name, row[0], sizeof(active_mi->master_log_name)); - active_mi->master_log_pos= my_strtoll10(row[1], (char**) 0, &error); + active_mi->master_log_pos= my_strtoll10(row[1], (char**) 0, &error_2); /* at least in recent versions, the condition below should be false */ if (active_mi->master_log_pos < BIN_LOG_HEADER_SIZE) active_mi->master_log_pos = BIN_LOG_HEADER_SIZE; diff --git a/sql/repl_failsafe.h b/sql/repl_failsafe.h index 19849e63af9..561db00d841 100644 --- a/sql/repl_failsafe.h +++ b/sql/repl_failsafe.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB & Sasha +/* Copyright (C) 2001-2005 MySQL AB & Sasha 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/rpl_filter.cc b/sql/rpl_filter.cc index c01b5189887..f76f798f6a6 100644 --- a/sql/rpl_filter.cc +++ b/sql/rpl_filter.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/rpl_filter.h b/sql/rpl_filter.h index 718fd401c56..396207d3a28 100644 --- a/sql/rpl_filter.h +++ b/sql/rpl_filter.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc index 5a74fd58755..95b5ecba895 100644 --- a/sql/rpl_injector.cc +++ b/sql/rpl_injector.cc @@ -1,10 +1,8 @@ -/* - Copyright (C) 2005 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -13,11 +11,10 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mysql_priv.h" #include "rpl_injector.h" -#ifdef HAVE_ROW_BASED_REPLICATION /* injector::transaction - member definitions @@ -39,6 +36,8 @@ injector::transaction::transaction(MYSQL_BIN_LOG *log, THD *thd) m_start_pos.m_file_pos= log_info.pos; begin_trans(m_thd); + + thd->set_current_stmt_binlog_row_based(); } injector::transaction::~transaction() @@ -189,5 +188,3 @@ void injector::new_trans(THD *thd, injector::transaction *ptr) DBUG_VOID_RETURN; } - -#endif diff --git a/sql/rpl_injector.h b/sql/rpl_injector.h index 48df30e8ac8..61c2e0ecebc 100644 --- a/sql/rpl_injector.h +++ b/sql/rpl_injector.h @@ -1,10 +1,8 @@ -/* - Copyright (C) 2005 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -13,7 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef INJECTOR_H #define INJECTOR_H @@ -21,13 +19,12 @@ /* Pull in 'byte', 'my_off_t', and 'uint32' */ #include <my_global.h> -#ifdef HAVE_ROW_BASED_REPLICATION #include <my_bitmap.h> /* Forward declarations */ class handler; class MYSQL_BIN_LOG; -class st_table; +struct st_table; typedef st_table TABLE; @@ -287,12 +284,14 @@ public: */ int check_state(enum_state const target_state) { +#ifndef DBUG_OFF static char const *state_name[] = { "START_STATE", "TABLE_STATE", "ROW_STATE", "STATE_COUNT" }; DBUG_ASSERT(0 <= target_state && target_state <= STATE_COUNT); DBUG_PRINT("info", ("In state %s", state_name[m_state])); +#endif if (m_state <= target_state && target_state <= m_state + 1 && m_state < STATE_COUNT) @@ -331,5 +330,4 @@ private: */ }; -#endif /* HAVE_ROW_BASED_REPLICATION */ #endif /* INJECTOR_H */ diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index c89c8aa131e..1c426eff768 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h index f0a7d6681fe..ae77e64d93a 100644 --- a/sql/rpl_mi.h +++ b/sql/rpl_mi.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 182592ac403..16e13f049e3 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +18,7 @@ #include "rpl_rli.h" #include <my_dir.h> // For MY_STAT #include "sql_repl.h" // For check_binlog_magic +#include "rpl_utility.h" static int count_relay_log_space(RELAY_LOG_INFO* rli); @@ -403,7 +403,7 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, bool look_for_description_event) { DBUG_ENTER("init_relay_log_pos"); - DBUG_PRINT("info", ("pos=%lu", pos)); + DBUG_PRINT("info", ("pos: %lu", (ulong) pos)); *errmsg=0; pthread_mutex_t *log_lock=rli->relay_log.get_log_lock(); @@ -856,7 +856,7 @@ void st_relay_log_info::close_temporary_tables() Don't ask for disk deletion. For now, anyway they will be deleted when slave restarts, but it is a better intention to not delete them. */ - DBUG_PRINT("info", ("table: %p", table)); + DBUG_PRINT("info", ("table: 0x%lx", (long) table)); close_temporary(table, 1, 0); } save_temporary_tables= 0; @@ -1115,4 +1115,23 @@ void st_relay_log_info::cleanup_context(THD *thd, bool error) last_event_start_time= 0; DBUG_VOID_RETURN; } + +void st_relay_log_info::clear_tables_to_lock() +{ + while (tables_to_lock) + { + gptr to_free= reinterpret_cast<gptr>(tables_to_lock); + if (tables_to_lock->m_tabledef_valid) + { + tables_to_lock->m_tabledef.table_def::~table_def(); + tables_to_lock->m_tabledef_valid= FALSE; + } + tables_to_lock= + static_cast<RPL_TABLE_LIST*>(tables_to_lock->next_global); + tables_to_lock_count--; + my_free(to_free, MYF(MY_WME)); + } + DBUG_ASSERT(tables_to_lock == NULL && tables_to_lock_count == 0); +} + #endif diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index 6365df8cec5..3f06e108f6d 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -21,6 +20,8 @@ #include "rpl_tblmap.h" +struct RPL_TABLE_LIST; + /**************************************************************************** @@ -289,7 +290,7 @@ typedef struct st_relay_log_info group_relay_log_pos); } - TABLE_LIST *tables_to_lock; /* RBR: Tables to lock */ + RPL_TABLE_LIST *tables_to_lock; /* RBR: Tables to lock */ uint tables_to_lock_count; /* RBR: Count of tables to lock */ table_mapping m_table_map; /* RBR: Mapping table-id to table */ @@ -303,16 +304,7 @@ typedef struct st_relay_log_info bool cached_charset_compare(char *charset) const; void cleanup_context(THD *, bool); - void clear_tables_to_lock() { - while (tables_to_lock) - { - char *to_free= reinterpret_cast<gptr>(tables_to_lock); - tables_to_lock= tables_to_lock->next_global; - tables_to_lock_count--; - my_free(to_free, MYF(MY_WME)); - } - DBUG_ASSERT(tables_to_lock == NULL && tables_to_lock_count == 0); - } + void clear_tables_to_lock(); /* Used by row-based replication to detect that it should not stop at diff --git a/sql/rpl_tblmap.cc b/sql/rpl_tblmap.cc index a0272b23ee8..7df4bcbdde7 100644 --- a/sql/rpl_tblmap.cc +++ b/sql/rpl_tblmap.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -50,17 +49,17 @@ table_mapping::~table_mapping() st_table* table_mapping::get_table(ulong table_id) { DBUG_ENTER("table_mapping::get_table(ulong)"); - DBUG_PRINT("enter", ("table_id=%d", table_id)); + DBUG_PRINT("enter", ("table_id: %lu", table_id)); entry *e= find_entry(table_id); if (e) { - DBUG_PRINT("info", ("tid %d -> table %p (%s)", - table_id, e->table, + DBUG_PRINT("info", ("tid %lu -> table 0x%lx (%s)", + table_id, (long) e->table, MAYBE_TABLE_NAME(e->table))); DBUG_RETURN(e->table); } - DBUG_PRINT("info", ("tid %d is not mapped!", table_id)); + DBUG_PRINT("info", ("tid %lu is not mapped!", table_id)); DBUG_RETURN(NULL); } @@ -93,9 +92,9 @@ int table_mapping::expand() int table_mapping::set_table(ulong table_id, TABLE* table) { DBUG_ENTER("table_mapping::set_table(ulong,TABLE*)"); - DBUG_PRINT("enter", ("table_id=%d, table=%p (%s)", + DBUG_PRINT("enter", ("table_id: %lu table: 0x%lx (%s)", table_id, - table, MAYBE_TABLE_NAME(table))); + (long) table, MAYBE_TABLE_NAME(table))); entry *e= find_entry(table_id); if (e == 0) { @@ -111,8 +110,8 @@ int table_mapping::set_table(ulong table_id, TABLE* table) e->table= table; my_hash_insert(&m_table_ids,(byte *)e); - DBUG_PRINT("info", ("tid %d -> table %p (%s)", - table_id, e->table, + DBUG_PRINT("info", ("tid %lu -> table 0x%lx (%s)", + table_id, (long) e->table, MAYBE_TABLE_NAME(e->table))); DBUG_RETURN(0); // All OK } diff --git a/sql/rpl_tblmap.h b/sql/rpl_tblmap.h index 23864bd329e..dbc968d0f67 100644 --- a/sql/rpl_tblmap.h +++ b/sql/rpl_tblmap.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc index fb3d689f2f7..1d7cc808f0c 100644 --- a/sql/rpl_utility.cc +++ b/sql/rpl_utility.cc @@ -1,9 +1,8 @@ -/* Copyright 2006 MySQL AB. All rights reserved. +/* Copyright (C) 2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -12,7 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "rpl_utility.h" @@ -25,7 +24,7 @@ field_length_from_packed(enum_field_types const field_type, switch (field_type) { case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: - length= ~0UL; + length= ~(uint32) 0; break; case MYSQL_TYPE_YEAR: case MYSQL_TYPE_TINY: @@ -71,7 +70,7 @@ field_length_from_packed(enum_field_types const field_type, break; break; case MYSQL_TYPE_BIT: - length= ~0UL; + length= ~(uint32) 0; break; default: /* This case should never be chosen */ @@ -85,7 +84,7 @@ field_length_from_packed(enum_field_types const field_type, case MYSQL_TYPE_SET: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VARCHAR: - length= ~0UL; // NYI + length= ~(uint32) 0; // NYI break; case MYSQL_TYPE_TINY_BLOB: @@ -93,7 +92,7 @@ field_length_from_packed(enum_field_types const field_type, case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_GEOMETRY: - length= ~0UL; // NYI + length= ~(uint32) 0; // NYI break; } @@ -132,7 +131,8 @@ table_def::compatible_with(RELAY_LOG_INFO const *rli_arg, TABLE *table) slave_print_msg(ERROR_LEVEL, rli, ER_BINLOG_ROW_WRONG_TABLE_DEF, "Table width mismatch - " "received %u columns, %s.%s has %u columns", - size(), tsh->db.str, tsh->table_name.str, tsh->fields); + (uint) size(), tsh->db.str, tsh->table_name.str, + tsh->fields); } for (uint col= 0 ; col < cols_to_check ; ++col) diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h index a45a16f1ca8..17879a9ecfc 100644 --- a/sql/rpl_utility.h +++ b/sql/rpl_utility.h @@ -1,9 +1,8 @@ -/* Copyright 2006 MySQL AB. All rights reserved. +/* Copyright (C) 2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -12,7 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RPL_UTILITY_H #define RPL_UTILITY_H @@ -23,98 +22,100 @@ #include "mysql_priv.h" +struct st_relay_log_info; +typedef st_relay_log_info RELAY_LOG_INFO; + uint32 -field_length_from_packed(enum_field_types const field_type, - byte const *const data); +field_length_from_packed(enum_field_types field_type, byte const *data); -/* +/** A table definition from the master. - RESPONSIBILITIES - + The responsibilities of this class is: - Extract and decode table definition data from the table map event - Check if table definition in table map is compatible with table definition on slave - DESCRIPTION - - Currently, the only field type data available is an array of the - type operators that are present in the table map event. + Currently, the only field type data available is an array of the + type operators that are present in the table map event. - TODO - - Add type operands to this structure to allow detection of - difference between, e.g., BIT(5) and BIT(10). + @todo Add type operands to this structure to allow detection of + difference between, e.g., BIT(5) and BIT(10). */ class table_def { public: - /* + /** Convenience declaration of the type of the field type data in a table map event. */ typedef unsigned char field_type; - /* + /** Constructor. - SYNOPSIS - table_def() - types Array of types - size Number of elements in array 'types' + @param types Array of types + @param size Number of elements in array 'types' */ table_def(field_type *types, my_size_t size) - : m_type(types), m_size(size) + : m_type(new unsigned char [size]), m_size(size) { + if (m_type) + memcpy(m_type, types, size); + else + m_size= 0; } - /* - Return the number of fields there is type data for. + ~table_def() { + if (m_type) + delete [] m_type; +#ifndef DBUG_OFF + m_type= 0; + m_size= 0; +#endif + } - SYNOPSIS - size() + /** + Return the number of fields there is type data for. - RETURN VALUE - The number of fields that there is type data for. + @return The number of fields that there is type data for. */ my_size_t size() const { return m_size; } + /* Return a representation of the type data for one field. - SYNOPSIS - type() - i Field index to return data for + @param index Field index to return data for - RETURN VALUE - - Will return a representation of the type data for field - 'i'. Currently, only the type identifier is returned. + @return Will return a representation of the type data for field + <code>index</code>. Currently, only the type identifier is + returned. */ - field_type type(my_ptrdiff_t i) const { return m_type[i]; } + field_type type(my_ptrdiff_t index) const + { + DBUG_ASSERT(0 <= index); + DBUG_ASSERT(static_cast<my_size_t>(index) < m_size); + return m_type[index]; + } - /* + /** Decide if the table definition is compatible with a table. - SYNOPSIS - compatible_with() - rli Pointer to relay log info - table Pointer to table to compare with. - - DESCRIPTION - - Compare the definition with a table to see if it is compatible - with it. A table definition is compatible with a table if: + Compare the definition with a table to see if it is compatible + with it. + A table definition is compatible with a table if: - the columns types of the table definition is a (not necessarily proper) prefix of the column type of the table, or - - the other way around - RETURN VALUE - 1 if the table definition is not compatible with 'table' - 0 if the table definition is compatible with 'table' + @param rli Pointer to relay log info + @param table Pointer to table to compare with. + + @retval 1 if the table definition is not compatible with @c table + @retval 0 if the table definition is compatible with @c table */ int compatible_with(RELAY_LOG_INFO const *rli, TABLE *table) const; @@ -123,4 +124,15 @@ private: field_type *m_type; // Array of type descriptors }; +/** + Extend the normal table list with a few new fields needed by the + slave thread, but nowhere else. + */ +struct RPL_TABLE_LIST + : public st_table_list +{ + bool m_tabledef_valid; + table_def m_tabledef; +}; + #endif /* RPL_UTILITY_H */ diff --git a/sql/scheduler.cc b/sql/scheduler.cc new file mode 100644 index 00000000000..b05bdf4756f --- /dev/null +++ b/sql/scheduler.cc @@ -0,0 +1,88 @@ +/* Copyright (C) 2007 MySQL 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 + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Implementation for the thread scheduler +*/ + +#ifdef USE_PRAGMA_INTERFACE +#pragma implementation +#endif + +#include <mysql_priv.h> + +/* + 'Dummy' functions to be used when we don't need any handling for a scheduler + event + */ + +static bool init_dummy(void) {return 0;} +static void post_kill_dummy(THD* thd) {} +static void end_dummy(void) {} +static bool end_thread_dummy(THD *thd, bool cache_thread) { return 0; } + +/* + Initialize default scheduler with dummy functions so that setup functions + only need to declare those that are relvant for their usage +*/ + +scheduler_functions::scheduler_functions() + :init(init_dummy), + init_new_connection_thread(init_new_connection_handler_thread), + add_connection(0), // Must be defined + post_kill_notification(post_kill_dummy), + end_thread(end_thread_dummy), end(end_dummy) +{} + + +/* + End connection, in case when we are using 'no-threads' +*/ + +static bool no_threads_end(THD *thd, bool put_in_cache) +{ + unlink_thd(thd); + pthread_mutex_unlock(&LOCK_thread_count); + return 1; // Abort handle_one_connection +} + + +/* + Initailize scheduler for --thread-handling=no-threads +*/ + +void one_thread_scheduler(scheduler_functions* func) +{ + func->max_threads= 1; +#ifndef EMBEDDED_LIBRARY + func->add_connection= handle_connection_in_main_thread; +#endif + func->init_new_connection_thread= init_dummy; + func->end_thread= no_threads_end; +} + + +/* + Initialize scheduler for --thread-handling=one-thread-per-connection +*/ + +#ifndef EMBEDDED_LIBRARY +void one_thread_per_connection_scheduler(scheduler_functions* func) +{ + func->max_threads= max_connections; + func->add_connection= create_thread_to_handle_connection; + func->end_thread= one_thread_per_connection_end; +} +#endif /* EMBEDDED_LIBRARY */ diff --git a/sql/scheduler.h b/sql/scheduler.h new file mode 100644 index 00000000000..8351cefda4c --- /dev/null +++ b/sql/scheduler.h @@ -0,0 +1,60 @@ +/* Copyright (C) 2007 MySQL 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 + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Classes for the thread scheduler +*/ + +#ifdef USE_PRAGMA_INTERFACE +#pragma interface +#endif + +class THD; + +/* Functions used when manipulating threads */ + +class scheduler_functions +{ +public: + uint max_threads; + bool (*init)(void); + bool (*init_new_connection_thread)(void); + void (*add_connection)(THD *thd); + void (*post_kill_notification)(THD *thd); + bool (*end_thread)(THD *thd, bool cache_thread); + void (*end)(void); + scheduler_functions(); +}; + +enum scheduler_types +{ + SCHEDULER_ONE_THREAD_PER_CONNECTION=1, + SCHEDULER_NO_THREADS, + SCHEDULER_POOL_OF_THREADS +}; + +void one_thread_per_connection_scheduler(scheduler_functions* func); +void one_thread_scheduler(scheduler_functions* func); + +enum pool_command_op +{ + NOT_IN_USE_OP= 0, NORMAL_OP= 1, CONNECT_OP, KILL_OP, DIE_OP +}; + +#define HAVE_POOL_OF_THREADS 0 /* For easyer tests */ +#define pool_of_threads_scheduler(A) one_thread_per_connection_scheduler(A) + +class thd_scheduler +{}; diff --git a/sql/set_var.cc b/sql/set_var.cc index 4369c288cd5..54db43deeb0 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -67,7 +66,6 @@ extern longlong innobase_log_file_size; extern long innobase_log_buffer_size; extern longlong innobase_buffer_pool_size; extern long innobase_additional_mem_pool_size; -extern long innobase_buffer_pool_awe_mem_mb; extern long innobase_file_io_threads, innobase_lock_wait_timeout; extern long innobase_force_recovery; extern long innobase_open_files; @@ -79,7 +77,9 @@ extern my_bool innobase_log_archive, innobase_use_doublewrite, innobase_use_checksums, innobase_file_per_table, - innobase_locks_unsafe_for_binlog; + innobase_locks_unsafe_for_binlog, + innobase_rollback_on_timeout, + innobase_stats_on_metadata; extern "C" { extern ulong srv_max_buf_pool_modified_pct; @@ -117,9 +117,6 @@ TYPELIB delay_key_write_typelib= delay_key_write_type_names, NULL }; -static int sys_check_charset(THD *thd, set_var *var); -static bool sys_update_charset(THD *thd, set_var *var); -static void sys_set_default_charset(THD *thd, enum_var_type type); static int sys_check_ftb_syntax(THD *thd, set_var *var); static bool sys_update_ftb_syntax(THD *thd, set_var * var); static void sys_default_ftb_syntax(THD *thd, enum_var_type type); @@ -156,7 +153,6 @@ static KEY_CACHE *create_key_cache(const char *name, uint length); void fix_sql_mode_var(THD *thd, enum_var_type type); static byte *get_error_count(THD *thd); static byte *get_warning_count(THD *thd); -static byte *get_prepared_stmt_count(THD *thd); static byte *get_tmpdir(THD *thd); static int sys_check_log_path(THD *thd, set_var *var); static bool sys_update_general_log_path(THD *thd, set_var * var); @@ -300,7 +296,7 @@ sys_var_thd_ulong sys_max_delayed_threads("max_delayed_threads", fix_max_connections); sys_var_thd_ulong sys_max_error_count("max_error_count", &SV::max_error_count); -sys_var_thd_ulong sys_max_heap_table_size("max_heap_table_size", +sys_var_thd_ulonglong sys_max_heap_table_size("max_heap_table_size", &SV::max_heap_table_size); sys_var_thd_ulong sys_pseudo_thread_id("pseudo_thread_id", &SV::pseudo_thread_id, @@ -371,7 +367,7 @@ sys_var_thd_ulong sys_preload_buff_size("preload_buffer_size", &SV::preload_buff_size); sys_var_thd_ulong sys_read_buff_size("read_buffer_size", &SV::read_buff_size); -sys_var_bool_ptr sys_readonly("read_only", &opt_readonly); +sys_var_opt_readonly sys_readonly("read_only", &opt_readonly); sys_var_thd_ulong sys_read_rnd_buff_size("read_rnd_buffer_size", &SV::read_rnd_buff_size); sys_var_thd_ulong sys_div_precincrement("div_precision_increment", @@ -401,6 +397,10 @@ sys_var_thd_ulong sys_trans_alloc_block_size("transaction_alloc_block_size", sys_var_thd_ulong sys_trans_prealloc_size("transaction_prealloc_size", &SV::trans_prealloc_size, 0, fix_trans_mem_root); +sys_var_thd_enum sys_thread_handling("thread_handling", + &SV::thread_handling, + &thread_handling_typelib, + NULL); #ifdef HAVE_QUERY_CACHE sys_var_long_ptr sys_query_cache_limit("query_cache_limit", @@ -416,6 +416,8 @@ sys_query_cache_wlock_invalidate("query_cache_wlock_invalidate", &SV::query_cache_wlock_invalidate); #endif /* HAVE_QUERY_CACHE */ sys_var_bool_ptr sys_secure_auth("secure_auth", &opt_secure_auth); +sys_var_const_str_ptr sys_secure_file_priv("secure_file_priv", + &opt_secure_file_priv); sys_var_long_ptr sys_server_id("server_id", &server_id, fix_server_id); sys_var_bool_ptr sys_slave_compressed_protocol("slave_compressed_protocol", &opt_slave_compressed_protocol); @@ -469,12 +471,16 @@ sys_var_long_ptr sys_table_lock_wait_timeout("table_lock_wait_timeout", &table_lock_wait_timeout); sys_var_long_ptr sys_thread_cache_size("thread_cache_size", &thread_cache_size); +#if HAVE_POOL_OF_THREADS == 1 +sys_var_long_ptr sys_thread_pool_size("thread_pool_size", + &thread_pool_size); +#endif sys_var_thd_enum sys_tx_isolation("tx_isolation", &SV::tx_isolation, &tx_isolation_typelib, fix_tx_isolation, check_tx_isolation); -sys_var_thd_ulong sys_tmp_table_size("tmp_table_size", +sys_var_thd_ulonglong sys_tmp_table_size("tmp_table_size", &SV::tmp_table_size); sys_var_bool_ptr sys_timed_mutexes("timed_mutexes", &timed_mutexes); @@ -640,9 +646,6 @@ static sys_var_readonly sys_warning_count("warning_count", OPT_SESSION, SHOW_LONG, get_warning_count); -static sys_var_readonly sys_prepared_stmt_count("prepared_stmt_count", - OPT_GLOBAL, SHOW_LONG, - get_prepared_stmt_count); /* alias for last_insert_id() to be compatible with Sybase */ #ifdef HAVE_REPLICATION @@ -661,32 +664,27 @@ sys_var_thd_time_zone sys_time_zone("time_zone"); /* Read only variables */ -sys_var_have_variable sys_have_archive_db("have_archive", &have_archive_db); -sys_var_have_variable sys_have_blackhole_db("have_blackhole_engine", - &have_blackhole_db); +/* Global read-only variable containing hostname */ +sys_var_const_str sys_hostname("hostname", glob_hostname); + + sys_var_have_variable sys_have_compress("have_compress", &have_compress); sys_var_have_variable sys_have_crypt("have_crypt", &have_crypt); sys_var_have_variable sys_have_csv_db("have_csv", &have_csv_db); sys_var_have_variable sys_have_dlopen("have_dynamic_loading", &have_dlopen); -sys_var_have_variable sys_have_example_db("have_example_engine", - &have_example_db); -sys_var_have_variable sys_have_federated_db("have_federated_engine", - &have_federated_db); sys_var_have_variable sys_have_geometry("have_geometry", &have_geometry); sys_var_have_variable sys_have_innodb("have_innodb", &have_innodb); -sys_var_have_variable sys_have_merge_db("have_merge", &have_merge_db); sys_var_have_variable sys_have_ndbcluster("have_ndbcluster", &have_ndbcluster); -sys_var_have_variable sys_have_openssl("have_openssl", &have_openssl); +sys_var_have_variable sys_have_openssl("have_openssl", &have_ssl); +sys_var_have_variable sys_have_ssl("have_ssl", &have_ssl); sys_var_have_variable sys_have_partition_db("have_partitioning", &have_partition_db); sys_var_have_variable sys_have_query_cache("have_query_cache", &have_query_cache); sys_var_have_variable sys_have_rtree_keys("have_rtree_keys", &have_rtree_keys); sys_var_have_variable sys_have_symlink("have_symlink", &have_symlink); -sys_var_have_variable sys_have_row_based_replication("have_row_based_replication",&have_row_based_replication); /* Global read-only variable describing server license */ sys_var_const_str sys_license("license", STRINGIFY_ARG(LICENSE)); - /* Global variables which enable|disable logging */ sys_var_log_state sys_var_general_log("general_log", &opt_log, QUERY_LOG_GENERAL); @@ -743,7 +741,7 @@ static int show_slave_skip_errors(THD *thd, SHOW_VAR *var, char *buff) #endif /* HAVE_REPLICATION */ /* - Variables shown by SHOW variables in alphabetical order + Variables shown by SHOW VARIABLES in alphabetical order */ SHOW_VAR init_vars[]= { @@ -795,22 +793,18 @@ SHOW_VAR init_vars[]= { {sys_var_general_log.name, (char*) &opt_log, SHOW_MY_BOOL}, {sys_var_general_log_path.name, (char*) &sys_var_general_log_path, SHOW_SYS}, {sys_group_concat_max_len.name, (char*) &sys_group_concat_max_len, SHOW_SYS}, - {sys_have_archive_db.name, (char*) &have_archive_db, SHOW_HAVE}, - {sys_have_blackhole_db.name,(char*) &have_blackhole_db, SHOW_HAVE}, + {sys_hostname.name, (char*) &sys_hostname, SHOW_SYS}, {sys_have_compress.name, (char*) &have_compress, SHOW_HAVE}, {sys_have_crypt.name, (char*) &have_crypt, SHOW_HAVE}, {sys_have_csv_db.name, (char*) &have_csv_db, SHOW_HAVE}, {sys_have_dlopen.name, (char*) &have_dlopen, SHOW_HAVE}, - {sys_have_example_db.name, (char*) &have_example_db, SHOW_HAVE}, - {sys_have_federated_db.name,(char*) &have_federated_db, SHOW_HAVE}, {sys_have_geometry.name, (char*) &have_geometry, SHOW_HAVE}, {sys_have_innodb.name, (char*) &have_innodb, SHOW_HAVE}, - {sys_have_merge_db.name, (char*) &have_merge_db, SHOW_HAVE}, {sys_have_ndbcluster.name, (char*) &have_ndbcluster, SHOW_HAVE}, - {sys_have_openssl.name, (char*) &have_openssl, SHOW_HAVE}, + {sys_have_openssl.name, (char*) &have_ssl, SHOW_HAVE}, + {sys_have_ssl.name, (char*) &have_ssl, SHOW_HAVE}, {sys_have_partition_db.name,(char*) &have_partition_db, SHOW_HAVE}, {sys_have_query_cache.name, (char*) &have_query_cache, SHOW_HAVE}, - {sys_have_row_based_replication.name, (char*) &have_row_based_replication, SHOW_HAVE}, {sys_have_rtree_keys.name, (char*) &have_rtree_keys, SHOW_HAVE}, {sys_have_symlink.name, (char*) &have_symlink, SHOW_HAVE}, {"init_connect", (char*) &sys_init_connect, SHOW_SYS}, @@ -819,7 +813,6 @@ SHOW_VAR init_vars[]= { #ifdef WITH_INNOBASE_STORAGE_ENGINE {"innodb_additional_mem_pool_size", (char*) &innobase_additional_mem_pool_size, SHOW_LONG }, {sys_innodb_autoextend_increment.name, (char*) &sys_innodb_autoextend_increment, SHOW_SYS}, - {"innodb_buffer_pool_awe_mem_mb", (char*) &innobase_buffer_pool_awe_mem_mb, SHOW_LONG }, {"innodb_buffer_pool_size", (char*) &innobase_buffer_pool_size, SHOW_LONGLONG }, {"innodb_checksums", (char*) &innobase_use_checksums, SHOW_MY_BOOL}, {sys_innodb_commit_concurrency.name, (char*) &sys_innodb_commit_concurrency, SHOW_SYS}, @@ -844,6 +837,8 @@ SHOW_VAR init_vars[]= { {sys_innodb_max_purge_lag.name, (char*) &sys_innodb_max_purge_lag, SHOW_SYS}, {"innodb_mirrored_log_groups", (char*) &innobase_mirrored_log_groups, SHOW_LONG}, {"innodb_open_files", (char*) &innobase_open_files, SHOW_LONG }, + {"innodb_rollback_on_timeout", (char*) &innobase_rollback_on_timeout, SHOW_MY_BOOL}, + {"innodb_stats_on_metadata", (char*) &innobase_stats_on_metadata, SHOW_MY_BOOL}, {sys_innodb_support_xa.name, (char*) &sys_innodb_support_xa, SHOW_SYS}, {sys_innodb_sync_spin_loops.name, (char*) &sys_innodb_sync_spin_loops, SHOW_SYS}, {sys_innodb_table_locks.name, (char*) &sys_innodb_table_locks, SHOW_SYS}, @@ -958,7 +953,6 @@ SHOW_VAR init_vars[]= { {"plugin_dir", (char*) opt_plugin_dir, SHOW_CHAR}, {"port", (char*) &mysqld_port, SHOW_INT}, {sys_preload_buff_size.name, (char*) &sys_preload_buff_size, SHOW_SYS}, - {sys_prepared_stmt_count.name, (char*) &sys_prepared_stmt_count, SHOW_SYS}, {"protocol_version", (char*) &protocol_version, SHOW_INT}, {sys_query_alloc_block_size.name, (char*) &sys_query_alloc_block_size, SHOW_SYS}, @@ -983,6 +977,7 @@ SHOW_VAR init_vars[]= { #endif {sys_rpl_recovery_rank.name,(char*) &sys_rpl_recovery_rank, SHOW_SYS}, {"secure_auth", (char*) &sys_secure_auth, SHOW_SYS}, + {"secure_file_priv", (char*) &sys_secure_file_priv, SHOW_SYS}, #ifdef HAVE_SMEM {"shared_memory", (char*) &opt_enable_shared_memory, SHOW_MY_BOOL}, {"shared_memory_base_name", (char*) &shared_memory_base_name, SHOW_CHAR_PTR}, @@ -1031,6 +1026,10 @@ SHOW_VAR init_vars[]= { #ifdef HAVE_THR_SETCONCURRENCY {"thread_concurrency", (char*) &concurrency, SHOW_LONG}, #endif + {sys_thread_handling.name, (char*) &sys_thread_handling, SHOW_SYS}, +#if HAVE_POOL_OF_THREADS == 1 + {sys_thread_pool_size.name, (char*) &sys_thread_pool_size, SHOW_SYS}, +#endif {"thread_stack", (char*) &thread_stack, SHOW_LONG}, {sys_time_format.name, (char*) &sys_time_format, SHOW_SYS}, {"time_zone", (char*) &sys_time_zone, SHOW_SYS}, @@ -1242,14 +1241,14 @@ static int check_completion_type(THD *thd, set_var *var) static void fix_net_read_timeout(THD *thd, enum_var_type type) { if (type != OPT_GLOBAL) - thd->net.read_timeout=thd->variables.net_read_timeout; + net_set_read_timeout(&thd->net, thd->variables.net_read_timeout); } static void fix_net_write_timeout(THD *thd, enum_var_type type) { if (type != OPT_GLOBAL) - thd->net.write_timeout=thd->variables.net_write_timeout; + net_set_write_timeout(&thd->net, thd->variables.net_write_timeout); } static void fix_net_retry_count(THD *thd, enum_var_type type) @@ -1326,10 +1325,6 @@ bool sys_var_thd_binlog_format::is_readonly() const If we don't have row-based replication compiled in, the variable is always read-only. */ -#ifndef HAVE_ROW_BASED_REPLICATION - my_error(ER_RBR_NOT_AVAILABLE, MYF(0)); - return 1; -#else if ((thd->variables.binlog_format == BINLOG_FORMAT_ROW) && thd->temporary_tables) { @@ -1354,16 +1349,13 @@ bool sys_var_thd_binlog_format::is_readonly() const return 1; } #endif /* HAVE_NDB_BINLOG */ -#endif /* HAVE_ROW_BASED_REPLICATION */ return sys_var_thd_enum::is_readonly(); } void fix_binlog_format_after_update(THD *thd, enum_var_type type) { -#ifdef HAVE_ROW_BASED_REPLICATION thd->reset_current_stmt_binlog_row_based(); -#endif /*HAVE_ROW_BASED_REPLICATION*/ } @@ -1442,9 +1434,9 @@ static void fix_server_id(THD *thd, enum_var_type type) sys_var_long_ptr:: -sys_var_long_ptr(const char *name_arg, ulong *value_ptr, +sys_var_long_ptr(const char *name_arg, ulong *value_ptr_arg, sys_after_update_func after_update_arg) - :sys_var_long_ptr_global(name_arg, value_ptr, + :sys_var_long_ptr_global(name_arg, value_ptr_arg, &LOCK_global_system_variables, after_update_arg) {} @@ -1794,7 +1786,7 @@ Item *sys_var::item(THD *thd, enum_var_type var_type, LEX_STRING *base) /* As there was no local variable, return the global value */ var_type= OPT_GLOBAL; } - switch (type()) { + switch (show_type()) { case SHOW_INT: { uint value; @@ -2618,7 +2610,7 @@ bool update_sys_var_str_path(THD *thd, sys_var_str *var_str, file_log= logger.get_log_file_handler(); break; default: - DBUG_ASSERT(0); + assert(0); // Impossible } if (!old_value) @@ -2645,7 +2637,7 @@ bool update_sys_var_str_path(THD *thd, sys_var_str *var_str, { switch (log_type) { case QUERY_LOG_SLOW: - file_log->open_slow_log(sys_var_general_log_path.value); + file_log->open_slow_log(sys_var_slow_log_path.value); break; case QUERY_LOG_GENERAL: file_log->open_query_log(sys_var_general_log_path.value); @@ -3005,17 +2997,39 @@ byte *sys_var_max_user_conn::value_ptr(THD *thd, enum_var_type type, return (byte*) &(max_user_connections); } + bool sys_var_thd_lc_time_names::check(THD *thd, set_var *var) { - char *locale_str =var->value->str_value.c_ptr(); - MY_LOCALE *locale_match= my_locale_by_name(locale_str); + MY_LOCALE *locale_match; - if (locale_match == NULL) + if (var->value->result_type() == INT_RESULT) { - my_printf_error(ER_UNKNOWN_ERROR, - "Unknown locale: '%s'", MYF(0), locale_str); - return 1; + if (!(locale_match= my_locale_by_number((uint) var->value->val_int()))) + { + char buf[20]; + int10_to_str((int) var->value->val_int(), buf, -10); + my_printf_error(ER_UNKNOWN_ERROR, "Unknown locale: '%s'", MYF(0), buf); + return 1; + } } + else // STRING_RESULT + { + char buff[6]; + String str(buff, sizeof(buff), &my_charset_latin1), *res; + if (!(res=var->value->val_str(&str))) + { + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, "NULL"); + return 1; + } + const char *locale_str= res->c_ptr(); + if (!(locale_match= my_locale_by_name(locale_str))) + { + my_printf_error(ER_UNKNOWN_ERROR, + "Unknown locale: '%s'", MYF(0), locale_str); + return 1; + } + } + var->save_result.locale_value= locale_match; return 0; } @@ -3059,7 +3073,7 @@ static bool set_option_autocommit(THD *thd, set_var *var) { /* The test is negative as the flag we use is NOT autocommit */ - ulong org_options=thd->options; + ulonglong org_options= thd->options; if (var->save_result.ulong_value != 0) thd->options&= ~((sys_var_thd_bit*) var->var)->bit_flag; @@ -3071,15 +3085,16 @@ static bool set_option_autocommit(THD *thd, set_var *var) if ((org_options & OPTION_NOT_AUTOCOMMIT)) { /* We changed to auto_commit mode */ - thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE | - OPTION_KEEP_LOG); + thd->options&= ~(ulonglong) (OPTION_BEGIN | + OPTION_STATUS_NO_TRANS_UPDATE | + OPTION_KEEP_LOG); thd->server_status|= SERVER_STATUS_AUTOCOMMIT; if (ha_commit(thd)) return 1; } else { - thd->options&= ~(ulong) (OPTION_STATUS_NO_TRANS_UPDATE); + thd->options&= ~(ulonglong) (OPTION_STATUS_NO_TRANS_UPDATE); thd->server_status&= ~SERVER_STATUS_AUTOCOMMIT; } } @@ -3141,6 +3156,7 @@ static byte *get_warning_count(THD *thd) { thd->sys_var_tmp.long_value= (thd->warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_NOTE] + + thd->warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_ERROR] + thd->warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_WARN]); return (byte*) &thd->sys_var_tmp.long_value; } @@ -3152,14 +3168,6 @@ static byte *get_error_count(THD *thd) return (byte*) &thd->sys_var_tmp.long_value; } -static byte *get_prepared_stmt_count(THD *thd) -{ - pthread_mutex_lock(&LOCK_prepared_stmt_count); - thd->sys_var_tmp.ulong_value= prepared_stmt_count; - pthread_mutex_unlock(&LOCK_prepared_stmt_count); - return (byte*) &thd->sys_var_tmp.ulong_value; -} - /* Get the tmpdir that was specified or chosen by default @@ -3640,7 +3648,8 @@ bool sys_var_thd_table_type::update(THD *thd, set_var *var) pointer to string with sql_mode representation */ -byte *sys_var_thd_sql_mode::symbolic_mode_representation(THD *thd, ulong val, +byte *sys_var_thd_sql_mode::symbolic_mode_representation(THD *thd, + ulonglong val, ulong *len) { char buff[256]; @@ -3668,8 +3677,8 @@ byte *sys_var_thd_sql_mode::symbolic_mode_representation(THD *thd, ulong val, byte *sys_var_thd_sql_mode::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { - ulong val= ((type == OPT_GLOBAL) ? global_system_variables.*offset : - thd->variables.*offset); + ulonglong val= ((type == OPT_GLOBAL) ? global_system_variables.*offset : + thd->variables.*offset); ulong length_unused; return symbolic_mode_representation(thd, val, &length_unused); } @@ -3881,6 +3890,70 @@ bool sys_var_trust_routine_creators::update(THD *thd, set_var *var) return sys_var_bool_ptr::update(thd, var); } +bool sys_var_opt_readonly::update(THD *thd, set_var *var) +{ + bool result; + + DBUG_ENTER("sys_var_opt_readonly::update"); + + /* Prevent self dead-lock */ + if (thd->locked_tables || thd->active_transaction()) + { + my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); + DBUG_RETURN(true); + } + + if (thd->global_read_lock) + { + /* + This connection already holds the global read lock. + This can be the case with: + - FLUSH TABLES WITH READ LOCK + - SET GLOBAL READ_ONLY = 1 + */ + result= sys_var_bool_ptr::update(thd, var); + DBUG_RETURN(result); + } + + /* + Perform a 'FLUSH TABLES WITH READ LOCK'. + This is a 3 step process: + - [1] lock_global_read_lock() + - [2] close_cached_tables() + - [3] make_global_read_lock_block_commit() + [1] prevents new connections from obtaining tables locked for write. + [2] waits until all existing connections close their tables. + [3] prevents transactions from being committed. + */ + + if (lock_global_read_lock(thd)) + DBUG_RETURN(true); + + /* + This call will be blocked by any connection holding a READ or WRITE lock. + Ideally, we want to wait only for pending WRITE locks, but since: + con 1> LOCK TABLE T FOR READ; + con 2> LOCK TABLE T FOR WRITE; (blocked by con 1) + con 3> SET GLOBAL READ ONLY=1; (blocked by con 2) + can cause to wait on a read lock, it's required for the client application + to unlock everything, and acceptable for the server to wait on all locks. + */ + if (result= close_cached_tables(thd, true, NULL, false)) + goto end_with_read_lock; + + if (result= make_global_read_lock_block_commit(thd)) + goto end_with_read_lock; + + /* Change the opt_readonly system variable, safe because the lock is held */ + result= sys_var_bool_ptr::update(thd, var); + +end_with_read_lock: + /* Release the lock */ + unlock_global_read_lock(thd); + DBUG_RETURN(result); +} + + /* even session variable here requires SUPER, because of -#o,file */ bool sys_var_thd_dbug::check(THD *thd, set_var *var) { @@ -3944,9 +4017,7 @@ sys_var_event_scheduler::update(THD *thd, set_var *var) DBUG_RETURN(TRUE); } - DBUG_PRINT("new_value", ("%lu", (bool)var->save_result.ulong_value)); - - Item_result var_type= var->value->result_type(); + DBUG_PRINT("info", ("new_value: %d", (int) var->save_result.ulong_value)); if (var->save_result.ulong_value == Events::EVENTS_ON) res= Events::get_instance()->start_execution_of_events(); @@ -3954,7 +4025,7 @@ sys_var_event_scheduler::update(THD *thd, set_var *var) res= Events::get_instance()->stop_execution_of_events(); else { - DBUG_ASSERT(0); + assert(0); // Impossible } if (res) my_error(ER_EVENT_SET_VAR_ERROR, MYF(0)); diff --git a/sql/set_var.h b/sql/set_var.h index 01669b378e1..8887d91ec69 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2002-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -65,8 +64,8 @@ public: bool check_enum(THD *thd, set_var *var, TYPELIB *enum_names); bool check_set(THD *thd, set_var *var, TYPELIB *enum_names); virtual bool update(THD *thd, set_var *var)=0; - virtual void set_default(THD *thd, enum_var_type type) {} - virtual SHOW_TYPE type() { return SHOW_UNDEF; } + virtual void set_default(THD *thd_arg, enum_var_type type) {} + virtual SHOW_TYPE show_type() { return SHOW_UNDEF; } virtual byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { return 0; } virtual bool check_type(enum_var_type type) @@ -106,14 +105,16 @@ class sys_var_long_ptr_global: public sys_var_global { public: ulong *value; - sys_var_long_ptr_global(const char *name_arg, ulong *value_ptr, + sys_var_long_ptr_global(const char *name_arg, ulong *value_ptr_arg, pthread_mutex_t *guard_arg, sys_after_update_func after_update_arg= NULL) - :sys_var_global(name_arg, after_update_arg, guard_arg), value(value_ptr) {} + :sys_var_global(name_arg, after_update_arg, guard_arg), + value(value_ptr_arg) + {} bool check(THD *thd, set_var *var); bool update(THD *thd, set_var *var); void set_default(THD *thd, enum_var_type type); - SHOW_TYPE type() { return SHOW_LONG; } + SHOW_TYPE show_type() { return SHOW_LONG; } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { return (byte*) value; } }; @@ -135,14 +136,14 @@ class sys_var_ulonglong_ptr :public sys_var { public: ulonglong *value; - sys_var_ulonglong_ptr(const char *name_arg, ulonglong *value_ptr) - :sys_var(name_arg),value(value_ptr) {} - sys_var_ulonglong_ptr(const char *name_arg, ulonglong *value_ptr, + sys_var_ulonglong_ptr(const char *name_arg, ulonglong *value_ptr_arg) + :sys_var(name_arg),value(value_ptr_arg) {} + sys_var_ulonglong_ptr(const char *name_arg, ulonglong *value_ptr_arg, sys_after_update_func func) - :sys_var(name_arg,func), value(value_ptr) {} + :sys_var(name_arg,func), value(value_ptr_arg) {} bool update(THD *thd, set_var *var); void set_default(THD *thd, enum_var_type type); - SHOW_TYPE type() { return SHOW_LONGLONG; } + SHOW_TYPE show_type() { return SHOW_LONGLONG; } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { return (byte*) value; } }; @@ -161,7 +162,7 @@ public: } bool update(THD *thd, set_var *var); void set_default(THD *thd, enum_var_type type); - SHOW_TYPE type() { return SHOW_MY_BOOL; } + SHOW_TYPE show_type() { return SHOW_MY_BOOL; } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { return (byte*) value; } bool check_update_type(Item_result type) { return 0; } @@ -193,7 +194,7 @@ public: { (*set_default_func)(thd, type); } - SHOW_TYPE type() { return SHOW_CHAR; } + SHOW_TYPE show_type() { return SHOW_CHAR; } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { return (byte*) value; } bool check_update_type(Item_result type) @@ -219,7 +220,7 @@ public: { return 1; } - SHOW_TYPE type() { return SHOW_CHAR; } + SHOW_TYPE show_type() { return SHOW_CHAR; } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { return (byte*) value; @@ -248,7 +249,7 @@ public: { return 1; } - SHOW_TYPE type() { return SHOW_CHAR; } + SHOW_TYPE show_type() { return SHOW_CHAR; } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { return (byte*) *value; @@ -276,7 +277,7 @@ public: return check_enum(thd, var, enum_names); } bool update(THD *thd, set_var *var); - SHOW_TYPE type() { return SHOW_CHAR; } + SHOW_TYPE show_type() { return SHOW_CHAR; } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); bool check_update_type(Item_result type) { return 0; } }; @@ -311,7 +312,7 @@ public: bool check(THD *thd, set_var *var); bool update(THD *thd, set_var *var); void set_default(THD *thd, enum_var_type type); - SHOW_TYPE type() { return SHOW_LONG; } + SHOW_TYPE show_type() { return SHOW_LONG; } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); }; @@ -329,7 +330,7 @@ public: {} bool update(THD *thd, set_var *var); void set_default(THD *thd, enum_var_type type); - SHOW_TYPE type() { return SHOW_HA_ROWS; } + SHOW_TYPE show_type() { return SHOW_HA_ROWS; } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); }; @@ -349,7 +350,7 @@ public: {} bool update(THD *thd, set_var *var); void set_default(THD *thd, enum_var_type type); - SHOW_TYPE type() { return SHOW_LONGLONG; } + SHOW_TYPE show_type() { return SHOW_LONGLONG; } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); bool check_default(enum_var_type type) { @@ -375,7 +376,7 @@ public: {} bool update(THD *thd, set_var *var); void set_default(THD *thd, enum_var_type type); - SHOW_TYPE type() { return SHOW_MY_BOOL; } + SHOW_TYPE show_type() { return SHOW_MY_BOOL; } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); bool check(THD *thd, set_var *var) { @@ -418,7 +419,7 @@ public: } bool update(THD *thd, set_var *var); void set_default(THD *thd, enum_var_type type); - SHOW_TYPE type() { return SHOW_CHAR; } + SHOW_TYPE show_type() { return SHOW_CHAR; } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); bool check_update_type(Item_result type) { return 0; } }; @@ -439,7 +440,7 @@ public: } void set_default(THD *thd, enum_var_type type); byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); - static byte *symbolic_mode_representation(THD *thd, ulong sql_mode, + static byte *symbolic_mode_representation(THD *thd, ulonglong sql_mode, ulong *length); }; @@ -453,7 +454,7 @@ public: :sys_var_thd(name_arg), offset(offset_arg) {} bool check(THD *thd, set_var *var); -SHOW_TYPE type() { return SHOW_CHAR; } + SHOW_TYPE show_type() { return SHOW_CHAR; } bool check_update_type(Item_result type) { return type != STRING_RESULT; /* Only accept strings */ @@ -479,11 +480,11 @@ class sys_var_thd_bit :public sys_var_thd sys_check_func check_func; sys_update_func update_func; public: - ulong bit_flag; + ulonglong bit_flag; bool reverse; sys_var_thd_bit(const char *name_arg, sys_check_func c_func, sys_update_func u_func, - ulong bit, bool reverse_arg=0) + ulonglong bit, bool reverse_arg=0) :sys_var_thd(name_arg), check_func(c_func), update_func(u_func), bit_flag(bit), reverse(reverse_arg) {} @@ -491,7 +492,7 @@ public: bool update(THD *thd, set_var *var); bool check_update_type(Item_result type) { return 0; } bool check_type(enum_var_type type) { return type == OPT_GLOBAL; } - SHOW_TYPE type() { return SHOW_MY_BOOL; } + SHOW_TYPE show_type() { return SHOW_MY_BOOL; } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); }; @@ -501,7 +502,7 @@ public: sys_var_thd_dbug(const char *name_arg) :sys_var_thd(name_arg) {} bool check_update_type(Item_result type) { return type != STRING_RESULT; } bool check(THD *thd, set_var *var); - SHOW_TYPE type() { return SHOW_CHAR; } + SHOW_TYPE show_type() { return SHOW_CHAR; } bool update(THD *thd, set_var *var); void set_default(THD *thd, enum_var_type type) { DBUG_POP(); } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *b); @@ -519,7 +520,7 @@ public: void set_default(THD *thd, enum_var_type type); bool check_type(enum_var_type type) { return type == OPT_GLOBAL; } bool check_default(enum_var_type type) { return 0; } - SHOW_TYPE type() { return SHOW_LONG; } + SHOW_TYPE show_type() { return SHOW_LONG; } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); }; @@ -530,7 +531,7 @@ public: sys_var_last_insert_id(const char *name_arg) :sys_var(name_arg) {} bool update(THD *thd, set_var *var); bool check_type(enum_var_type type) { return type == OPT_GLOBAL; } - SHOW_TYPE type() { return SHOW_LONGLONG; } + SHOW_TYPE show_type() { return SHOW_LONGLONG; } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); }; @@ -541,7 +542,7 @@ public: sys_var_insert_id(const char *name_arg) :sys_var(name_arg) {} bool update(THD *thd, set_var *var); bool check_type(enum_var_type type) { return type == OPT_GLOBAL; } - SHOW_TYPE type() { return SHOW_LONGLONG; } + SHOW_TYPE show_type() { return SHOW_LONGLONG; } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); }; @@ -556,15 +557,15 @@ public: bool check_type(enum_var_type type) { return type != OPT_GLOBAL; } /* We can't retrieve the value of this, so we don't have to define - type() or value_ptr() + show_type() or value_ptr() */ }; class sys_var_sync_binlog_period :public sys_var_long_ptr { public: - sys_var_sync_binlog_period(const char *name_arg, ulong *value_ptr) - :sys_var_long_ptr(name_arg,value_ptr) {} + sys_var_sync_binlog_period(const char *name_arg, ulong *value_ptr_arg) + :sys_var_long_ptr(name_arg,value_ptr_arg) {} bool update(THD *thd, set_var *var); }; #endif @@ -594,7 +595,7 @@ public: no_support_one_shot= 0; } bool check(THD *thd, set_var *var); -SHOW_TYPE type() { return SHOW_CHAR; } + SHOW_TYPE show_type() { return SHOW_CHAR; } bool check_update_type(Item_result type) { return ((type != STRING_RESULT) && (type != INT_RESULT)); @@ -618,7 +619,7 @@ public: no_support_one_shot= 0; } bool check(THD *thd, set_var *var); - SHOW_TYPE type() { return SHOW_CHAR; } + SHOW_TYPE show_type() { return SHOW_CHAR; } bool check_update_type(Item_result type) { return ((type != STRING_RESULT) && (type != INT_RESULT)); @@ -734,7 +735,7 @@ public: :sys_var_key_cache_param(name_arg, offsetof(KEY_CACHE, param_buff_size)) {} bool update(THD *thd, set_var *var); - SHOW_TYPE type() { return SHOW_LONGLONG; } + SHOW_TYPE show_type() { return SHOW_LONGLONG; } }; @@ -745,7 +746,7 @@ public: :sys_var_key_cache_param(name_arg, offset_arg) {} bool update(THD *thd, set_var *var); - SHOW_TYPE type() { return SHOW_LONG; } + SHOW_TYPE show_type() { return SHOW_LONG; } }; @@ -760,7 +761,7 @@ public: :sys_var_thd(name_arg), offset(offset_arg), date_time_type(date_time_type_arg) {} - SHOW_TYPE type() { return SHOW_CHAR; } + SHOW_TYPE show_type() { return SHOW_CHAR; } bool check_update_type(Item_result type) { return type != STRING_RESULT; /* Only accept strings */ @@ -802,7 +803,7 @@ public: byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); bool check_update_type(Item_result type) { return 0; } void set_default(THD *thd, enum_var_type type); - SHOW_TYPE type() { return SHOW_CHAR; } + SHOW_TYPE show_type() { return SHOW_CHAR; } }; @@ -812,13 +813,13 @@ class sys_var_readonly: public sys_var { public: enum_var_type var_type; - SHOW_TYPE show_type; + SHOW_TYPE show_type_value; sys_value_ptr_func value_ptr_func; sys_var_readonly(const char *name_arg, enum_var_type type, SHOW_TYPE show_type_arg, sys_value_ptr_func value_ptr_func_arg) :sys_var(name_arg), var_type(type), - show_type(show_type_arg), value_ptr_func(value_ptr_func_arg) + show_type_value(show_type_arg), value_ptr_func(value_ptr_func_arg) {} bool update(THD *thd, set_var *var) { return 1; } bool check_default(enum_var_type type) { return 1; } @@ -828,7 +829,7 @@ public: { return (*value_ptr_func)(thd); } - SHOW_TYPE type() { return show_type; } + SHOW_TYPE show_type() { return show_type_value; } bool is_readonly() const { return 1; } }; @@ -851,7 +852,7 @@ public: bool check_default(enum_var_type type) { return 1; } bool check_type(enum_var_type type) { return type != OPT_GLOBAL; } bool check_update_type(Item_result type) { return 1; } - SHOW_TYPE type() { return SHOW_CHAR; } + SHOW_TYPE show_type() { return SHOW_CHAR; } bool is_readonly() const { return 1; } }; @@ -865,7 +866,7 @@ public: no_support_one_shot= 0; } bool check(THD *thd, set_var *var); - SHOW_TYPE type() { return SHOW_CHAR; } + SHOW_TYPE show_type() { return SHOW_CHAR; } bool check_update_type(Item_result type) { return type != STRING_RESULT; /* Only accept strings */ @@ -889,7 +890,7 @@ public: return type != OPT_GLOBAL || !option_limits; } void set_default(THD *thd, enum_var_type type); - SHOW_TYPE type() { return SHOW_INT; } + SHOW_TYPE show_type() { return SHOW_INT; } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); }; @@ -905,17 +906,35 @@ public: }; +/** + Handler for setting the system variable --read-only. +*/ + +class sys_var_opt_readonly :public sys_var_bool_ptr +{ +public: + sys_var_opt_readonly(const char *name_arg, my_bool *value_arg) : + sys_var_bool_ptr(name_arg, value_arg) {}; + ~sys_var_opt_readonly() {}; + bool update(THD *thd, set_var *var); +}; + + class sys_var_thd_lc_time_names :public sys_var_thd { public: sys_var_thd_lc_time_names(const char *name_arg): - sys_var_thd(name_arg) - {} + sys_var_thd(name_arg) + { +#if MYSQL_VERSION_ID < 50000 + no_support_one_shot= 0; +#endif + } bool check(THD *thd, set_var *var); - SHOW_TYPE type() { return SHOW_CHAR; } + SHOW_TYPE show_type() { return SHOW_CHAR; } bool check_update_type(Item_result type) { - return type != STRING_RESULT; /* Only accept strings */ + return ((type != STRING_RESULT) && (type != INT_RESULT)); } bool check_default(enum_var_type type) { return 0; } bool update(THD *thd, set_var *var); @@ -932,7 +951,7 @@ public: sys_var_long_ptr(name_arg, NULL, NULL) {}; bool update(THD *thd, set_var *var); byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); - SHOW_TYPE type() { return SHOW_CHAR; } + SHOW_TYPE show_type() { return SHOW_CHAR; } bool check(THD *thd, set_var *var); bool check_update_type(Item_result type) { @@ -940,9 +959,7 @@ public: } }; -#ifdef HAVE_ROW_BASED_REPLICATION extern void fix_binlog_format_after_update(THD *thd, enum_var_type type); -#endif class sys_var_thd_binlog_format :public sys_var_thd_enum { @@ -950,9 +967,7 @@ public: sys_var_thd_binlog_format(const char *name_arg, ulong SV::*offset_arg) :sys_var_thd_enum(name_arg, offset_arg, &binlog_format_typelib -#ifdef HAVE_ROW_BASED_REPLICATION , fix_binlog_format_after_update -#endif ) {}; bool is_readonly() const; @@ -995,8 +1010,8 @@ public: } save_result; LEX_STRING base; /* for structs */ - set_var(enum_var_type type_arg, sys_var *var_arg, const LEX_STRING *base_name_arg, - Item *value_arg) + set_var(enum_var_type type_arg, sys_var *var_arg, + const LEX_STRING *base_name_arg, Item *value_arg) :var(var_arg), type(type_arg), base(*base_name_arg) { /* diff --git a/sql/share/Makefile.am b/sql/share/Makefile.am index 6d905ba35dc..68b393e619f 100644 --- a/sql/share/Makefile.am +++ b/sql/share/Makefile.am @@ -2,8 +2,7 @@ # # 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 -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/Index.xml b/sql/share/charsets/Index.xml index 3f1a25072b7..80b844e2f19 100644 --- a/sql/share/charsets/Index.xml +++ b/sql/share/charsets/Index.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/armscii8.xml b/sql/share/charsets/armscii8.xml index d0ab428345f..714e57bb12e 100644 --- a/sql/share/charsets/armscii8.xml +++ b/sql/share/charsets/armscii8.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/ascii.xml b/sql/share/charsets/ascii.xml index 3813bd42601..97006c53680 100644 --- a/sql/share/charsets/ascii.xml +++ b/sql/share/charsets/ascii.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/cp1250.xml b/sql/share/charsets/cp1250.xml index b83d0faeca8..bd0d7d3f3c0 100644 --- a/sql/share/charsets/cp1250.xml +++ b/sql/share/charsets/cp1250.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/cp1251.xml b/sql/share/charsets/cp1251.xml index 7f94788c0d0..b80db9f8ec0 100644 --- a/sql/share/charsets/cp1251.xml +++ b/sql/share/charsets/cp1251.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/cp1256.xml b/sql/share/charsets/cp1256.xml index 69eb6a68238..64cb253145c 100644 --- a/sql/share/charsets/cp1256.xml +++ b/sql/share/charsets/cp1256.xml @@ -9,8 +9,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/cp1257.xml b/sql/share/charsets/cp1257.xml index 93a1bd47a77..0c2688c264e 100644 --- a/sql/share/charsets/cp1257.xml +++ b/sql/share/charsets/cp1257.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/cp850.xml b/sql/share/charsets/cp850.xml index 79497aa17f1..4076a5f6a56 100644 --- a/sql/share/charsets/cp850.xml +++ b/sql/share/charsets/cp850.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/cp852.xml b/sql/share/charsets/cp852.xml index 73a81e54b02..25b622d2a4b 100644 --- a/sql/share/charsets/cp852.xml +++ b/sql/share/charsets/cp852.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/cp866.xml b/sql/share/charsets/cp866.xml index 1a72b396c7c..fa2e1865de6 100644 --- a/sql/share/charsets/cp866.xml +++ b/sql/share/charsets/cp866.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/dec8.xml b/sql/share/charsets/dec8.xml index 2cb28cb0f4f..2cd52de464a 100644 --- a/sql/share/charsets/dec8.xml +++ b/sql/share/charsets/dec8.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/geostd8.xml b/sql/share/charsets/geostd8.xml index c09aa078fb7..5e3816975d6 100644 --- a/sql/share/charsets/geostd8.xml +++ b/sql/share/charsets/geostd8.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/greek.xml b/sql/share/charsets/greek.xml index 1cfe6b49610..000019a8ce0 100644 --- a/sql/share/charsets/greek.xml +++ b/sql/share/charsets/greek.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/hebrew.xml b/sql/share/charsets/hebrew.xml index 5bcf222a728..20d68487301 100644 --- a/sql/share/charsets/hebrew.xml +++ b/sql/share/charsets/hebrew.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -40,7 +39,7 @@ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 - 02 02 02 02 02 02 02 02 02 02 02 00 00 00 00 00 + 02 02 02 02 02 02 02 02 02 02 02 00 00 20 20 00 </map> </ctype> @@ -106,7 +105,7 @@ 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 2017 05D0 05D1 05D2 05D3 05D4 05D5 05D6 05D7 05D8 05D9 05DA 05DB 05DC 05DD 05DE 05DF -05E0 05E1 05E2 05E3 05E4 05E5 05E6 05E7 05E8 05E9 05EA 0000 0000 0000 0000 0000 +05E0 05E1 05E2 05E3 05E4 05E5 05E6 05E7 05E8 05E9 05EA 0000 0000 200E 200F 0000 </map> </unicode> diff --git a/sql/share/charsets/hp8.xml b/sql/share/charsets/hp8.xml index 35224f8c544..3ab383ef386 100644 --- a/sql/share/charsets/hp8.xml +++ b/sql/share/charsets/hp8.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/keybcs2.xml b/sql/share/charsets/keybcs2.xml index 6332891ef23..7335a0f428d 100644 --- a/sql/share/charsets/keybcs2.xml +++ b/sql/share/charsets/keybcs2.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/koi8r.xml b/sql/share/charsets/koi8r.xml index 033597e9bfc..2d8473f6440 100644 --- a/sql/share/charsets/koi8r.xml +++ b/sql/share/charsets/koi8r.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/koi8u.xml b/sql/share/charsets/koi8u.xml index 4f5fa35af3d..16177627ffe 100644 --- a/sql/share/charsets/koi8u.xml +++ b/sql/share/charsets/koi8u.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/latin1.xml b/sql/share/charsets/latin1.xml index 5814a17b0e1..88ceff440d5 100644 --- a/sql/share/charsets/latin1.xml +++ b/sql/share/charsets/latin1.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/latin2.xml b/sql/share/charsets/latin2.xml index 7f00148a1df..6b887b927a4 100644 --- a/sql/share/charsets/latin2.xml +++ b/sql/share/charsets/latin2.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/latin5.xml b/sql/share/charsets/latin5.xml index 5004f045889..9c23200a46d 100644 --- a/sql/share/charsets/latin5.xml +++ b/sql/share/charsets/latin5.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/latin7.xml b/sql/share/charsets/latin7.xml index dd87a1a2d89..02d3ff8b17e 100644 --- a/sql/share/charsets/latin7.xml +++ b/sql/share/charsets/latin7.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/macce.xml b/sql/share/charsets/macce.xml index 61f6d79b34f..21e303609cf 100644 --- a/sql/share/charsets/macce.xml +++ b/sql/share/charsets/macce.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/macroman.xml b/sql/share/charsets/macroman.xml index 36c8e8cf13a..2b43fe73b07 100644 --- a/sql/share/charsets/macroman.xml +++ b/sql/share/charsets/macroman.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/charsets/swe7.xml b/sql/share/charsets/swe7.xml index 2b8ff4edcce..17fa6b7d9bc 100644 --- a/sql/share/charsets/swe7.xml +++ b/sql/share/charsets/swe7.xml @@ -7,8 +7,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 0bcd15c2b82..1d9c7c814fd 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -51,54 +51,54 @@ ER_YES spa "SI" ukr "ôáë" ER_CANT_CREATE_FILE - cze "Nemohu vytvo-Bøit soubor '%-.64s' (chybový kód: %d)" - dan "Kan ikke oprette filen '%-.64s' (Fejlkode: %d)" - nla "Kan file '%-.64s' niet aanmaken (Errcode: %d)" + cze "Nemohu vytvo-Bøit soubor '%-.200s' (chybový kód: %d)" + dan "Kan ikke oprette filen '%-.200s' (Fejlkode: %d)" + nla "Kan file '%-.200s' niet aanmaken (Errcode: %d)" eng "Can't create file '%-.200s' (errno: %d)" - est "Ei suuda luua faili '%-.64s' (veakood: %d)" - fre "Ne peut créer le fichier '%-.64s' (Errcode: %d)" - ger "Kann Datei '%-.64s' nicht erzeugen (Fehler: %d)" - greek "Áäýíáôç ç äçìéïõñãßá ôïõ áñ÷åßïõ '%-.64s' (êùäéêüò ëÜèïõò: %d)" - hun "A '%-.64s' file nem hozhato letre (hibakod: %d)" - ita "Impossibile creare il file '%-.64s' (errno: %d)" - jpn "'%-.64s' ¥Õ¥¡¥¤¥ë¤¬ºî¤ì¤Þ¤»¤ó (errno: %d)" - kor "ÈÀÏ '%-.64s'¸¦ ¸¸µéÁö ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)" - nor "Kan ikke opprette fila '%-.64s' (Feilkode: %d)" - norwegian-ny "Kan ikkje opprette fila '%-.64s' (Feilkode: %d)" - pol "Nie mo¿na stworzyæ pliku '%-.64s' (Kod b³êdu: %d)" - por "Não pode criar o arquivo '%-.64s' (erro no. %d)" - rum "Nu pot sa creez fisierul '%-.64s' (Eroare: %d)" - rus "îÅ×ÏÚÍÏÖÎÏ ÓÏÚÄÁÔØ ÆÁÊÌ '%-.64s' (ÏÛÉÂËÁ: %d)" - serbian "Ne mogu da kreiram file '%-.64s' (errno: %d)" - slo "Nemô¾em vytvori» súbor '%-.64s' (chybový kód: %d)" - spa "No puedo crear archivo '%-.64s' (Error: %d)" - swe "Kan inte skapa filen '%-.64s' (Felkod: %d)" - ukr "îÅ ÍÏÖÕ ÓÔ×ÏÒÉÔÉ ÆÁÊÌ '%-.64s' (ÐÏÍÉÌËÁ: %d)" + est "Ei suuda luua faili '%-.200s' (veakood: %d)" + fre "Ne peut créer le fichier '%-.200s' (Errcode: %d)" + ger "Kann Datei '%-.200s' nicht erzeugen (Fehler: %d)" + greek "Áäýíáôç ç äçìéïõñãßá ôïõ áñ÷åßïõ '%-.200s' (êùäéêüò ëÜèïõò: %d)" + hun "A '%-.200s' file nem hozhato letre (hibakod: %d)" + ita "Impossibile creare il file '%-.200s' (errno: %d)" + jpn "'%-.200s' ¥Õ¥¡¥¤¥ë¤¬ºî¤ì¤Þ¤»¤ó (errno: %d)" + kor "ÈÀÏ '%-.200s'¸¦ ¸¸µéÁö ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)" + nor "Kan ikke opprette fila '%-.200s' (Feilkode: %d)" + norwegian-ny "Kan ikkje opprette fila '%-.200s' (Feilkode: %d)" + pol "Nie mo¿na stworzyæ pliku '%-.200s' (Kod b³êdu: %d)" + por "Não pode criar o arquivo '%-.200s' (erro no. %d)" + rum "Nu pot sa creez fisierul '%-.200s' (Eroare: %d)" + rus "îÅ×ÏÚÍÏÖÎÏ ÓÏÚÄÁÔØ ÆÁÊÌ '%-.200s' (ÏÛÉÂËÁ: %d)" + serbian "Ne mogu da kreiram file '%-.200s' (errno: %d)" + slo "Nemô¾em vytvori» súbor '%-.200s' (chybový kód: %d)" + spa "No puedo crear archivo '%-.200s' (Error: %d)" + swe "Kan inte skapa filen '%-.200s' (Felkod: %d)" + ukr "îÅ ÍÏÖÕ ÓÔ×ÏÒÉÔÉ ÆÁÊÌ '%-.200s' (ÐÏÍÉÌËÁ: %d)" ER_CANT_CREATE_TABLE - cze "Nemohu vytvo-Bøit tabulku '%-.64s' (chybový kód: %d)" - dan "Kan ikke oprette tabellen '%-.64s' (Fejlkode: %d)" - nla "Kan tabel '%-.64s' niet aanmaken (Errcode: %d)" - eng "Can't create table '%-.64s' (errno: %d)" - jps "'%-.64s' ƒe[ƒuƒ‹‚ªì‚ê‚Ü‚¹‚ñ.(errno: %d)", - est "Ei suuda luua tabelit '%-.64s' (veakood: %d)" - fre "Ne peut créer la table '%-.64s' (Errcode: %d)" - ger "Kann Tabelle '%-.64s' nicht erzeugen (Fehler: %d)" - greek "Áäýíáôç ç äçìéïõñãßá ôïõ ðßíáêá '%-.64s' (êùäéêüò ëÜèïõò: %d)" - hun "A '%-.64s' tabla nem hozhato letre (hibakod: %d)" - ita "Impossibile creare la tabella '%-.64s' (errno: %d)" - jpn "'%-.64s' ¥Æ¡¼¥Ö¥ë¤¬ºî¤ì¤Þ¤»¤ó.(errno: %d)" - kor "Å×À̺í '%-.64s'¸¦ ¸¸µéÁö ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)" - nor "Kan ikke opprette tabellen '%-.64s' (Feilkode: %d)" - norwegian-ny "Kan ikkje opprette tabellen '%-.64s' (Feilkode: %d)" - pol "Nie mo¿na stworzyæ tabeli '%-.64s' (Kod b³êdu: %d)" - por "Não pode criar a tabela '%-.64s' (erro no. %d)" - rum "Nu pot sa creez tabla '%-.64s' (Eroare: %d)" - rus "îÅ×ÏÚÍÏÖÎÏ ÓÏÚÄÁÔØ ÔÁÂÌÉÃÕ '%-.64s' (ÏÛÉÂËÁ: %d)" - serbian "Ne mogu da kreiram tabelu '%-.64s' (errno: %d)" - slo "Nemô¾em vytvori» tabuµku '%-.64s' (chybový kód: %d)" - spa "No puedo crear tabla '%-.64s' (Error: %d)" - swe "Kan inte skapa tabellen '%-.64s' (Felkod: %d)" - ukr "îÅ ÍÏÖÕ ÓÔ×ÏÒÉÔÉ ÔÁÂÌÉÃÀ '%-.64s' (ÐÏÍÉÌËÁ: %d)" + cze "Nemohu vytvo-Bøit tabulku '%-.200s' (chybový kód: %d)" + dan "Kan ikke oprette tabellen '%-.200s' (Fejlkode: %d)" + nla "Kan tabel '%-.200s' niet aanmaken (Errcode: %d)" + eng "Can't create table '%-.200s' (errno: %d)" + jps "'%-.200s' ƒe[ƒuƒ‹‚ªì‚ê‚Ü‚¹‚ñ.(errno: %d)", + est "Ei suuda luua tabelit '%-.200s' (veakood: %d)" + fre "Ne peut créer la table '%-.200s' (Errcode: %d)" + ger "Kann Tabelle '%-.200s' nicht erzeugen (Fehler: %d)" + greek "Áäýíáôç ç äçìéïõñãßá ôïõ ðßíáêá '%-.200s' (êùäéêüò ëÜèïõò: %d)" + hun "A '%-.200s' tabla nem hozhato letre (hibakod: %d)" + ita "Impossibile creare la tabella '%-.200s' (errno: %d)" + jpn "'%-.200s' ¥Æ¡¼¥Ö¥ë¤¬ºî¤ì¤Þ¤»¤ó.(errno: %d)" + kor "Å×À̺í '%-.200s'¸¦ ¸¸µéÁö ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)" + nor "Kan ikke opprette tabellen '%-.200s' (Feilkode: %d)" + norwegian-ny "Kan ikkje opprette tabellen '%-.200s' (Feilkode: %d)" + pol "Nie mo¿na stworzyæ tabeli '%-.200s' (Kod b³êdu: %d)" + por "Não pode criar a tabela '%-.200s' (erro no. %d)" + rum "Nu pot sa creez tabla '%-.200s' (Eroare: %d)" + rus "îÅ×ÏÚÍÏÖÎÏ ÓÏÚÄÁÔØ ÔÁÂÌÉÃÕ '%-.200s' (ÏÛÉÂËÁ: %d)" + serbian "Ne mogu da kreiram tabelu '%-.200s' (errno: %d)" + slo "Nemô¾em vytvori» tabuµku '%-.200s' (chybový kód: %d)" + spa "No puedo crear tabla '%-.200s' (Error: %d)" + swe "Kan inte skapa tabellen '%-.200s' (Felkod: %d)" + ukr "îÅ ÍÏÖÕ ÓÔ×ÏÒÉÔÉ ÔÁÂÌÉÃÀ '%-.200s' (ÐÏÍÉÌËÁ: %d)" ER_CANT_CREATE_DB cze "Nemohu vytvo-Bøit databázi '%-.64s' (chybový kód: %d)" dan "Kan ikke oprette databasen '%-.64s' (Fejlkode: %d)" @@ -275,30 +275,30 @@ ER_CANT_FIND_SYSTEM_REC swe "Hittar inte posten i systemregistret" ukr "îÅ ÍÏÖÕ ÚÞÉÔÁÔÉ ÚÁÐÉÓ Ú ÓÉÓÔÅÍÎϧ ÔÁÂÌÉæ" ER_CANT_GET_STAT - cze "Nemohu z-Bískat stav '%-.64s' (chybový kód: %d)" - dan "Kan ikke læse status af '%-.64s' (Fejlkode: %d)" - nla "Kan de status niet krijgen van '%-.64s' (Errcode: %d)" + cze "Nemohu z-Bískat stav '%-.200s' (chybový kód: %d)" + dan "Kan ikke læse status af '%-.200s' (Fejlkode: %d)" + nla "Kan de status niet krijgen van '%-.200s' (Errcode: %d)" eng "Can't get status of '%-.200s' (errno: %d)" - jps "'%-.64s' ‚̃XƒeƒCƒ^ƒX‚ª“¾‚ç‚ê‚Ü‚¹‚ñ. (errno: %d)", - est "Ei suuda lugeda '%-.64s' olekut (veakood: %d)" - fre "Ne peut obtenir le status de '%-.64s' (Errcode: %d)" - ger "Kann Status von '%-.64s' nicht ermitteln (Fehler: %d)" - greek "Áäýíáôç ç ëÞøç ðëçñïöïñéþí ãéá ôçí êáôÜóôáóç ôïõ '%-.64s' (êùäéêüò ëÜèïõò: %d)" - hun "A(z) '%-.64s' statusza nem allapithato meg (hibakod: %d)" - ita "Impossibile leggere lo stato di '%-.64s' (errno: %d)" - jpn "'%-.64s' ¤Î¥¹¥Æ¥¤¥¿¥¹¤¬ÆÀ¤é¤ì¤Þ¤»¤ó. (errno: %d)" - kor "'%-.64s'ÀÇ »óŸ¦ ¾òÁö ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)" - nor "Kan ikke lese statusen til '%-.64s' (Feilkode: %d)" - norwegian-ny "Kan ikkje lese statusen til '%-.64s' (Feilkode: %d)" - pol "Nie mo¿na otrzymaæ statusu '%-.64s' (Kod b³êdu: %d)" - por "Não pode obter o status de '%-.64s' (erro no. %d)" - rum "Nu pot sa obtin statusul lui '%-.64s' (Eroare: %d)" - rus "îÅ×ÏÚÍÏÖÎÏ ÐÏÌÕÞÉÔØ ÓÔÁÔÕÓÎÕÀ ÉÎÆÏÒÍÁÃÉÀ Ï '%-.64s' (ÏÛÉÂËÁ: %d)" - serbian "Ne mogu da dobijem stanje file-a '%-.64s' (errno: %d)" - slo "Nemô¾em zisti» stav '%-.64s' (chybový kód: %d)" - spa "No puedo obtener el estado de '%-.64s' (Error: %d)" - swe "Kan inte läsa filinformationen (stat) från '%-.64s' (Felkod: %d)" - ukr "îÅ ÍÏÖÕ ÏÔÒÉÍÁÔÉ ÓÔÁÔÕÓ '%-.64s' (ÐÏÍÉÌËÁ: %d)" + jps "'%-.200s' ‚̃XƒeƒCƒ^ƒX‚ª“¾‚ç‚ê‚Ü‚¹‚ñ. (errno: %d)", + est "Ei suuda lugeda '%-.200s' olekut (veakood: %d)" + fre "Ne peut obtenir le status de '%-.200s' (Errcode: %d)" + ger "Kann Status von '%-.200s' nicht ermitteln (Fehler: %d)" + greek "Áäýíáôç ç ëÞøç ðëçñïöïñéþí ãéá ôçí êáôÜóôáóç ôïõ '%-.200s' (êùäéêüò ëÜèïõò: %d)" + hun "A(z) '%-.200s' statusza nem allapithato meg (hibakod: %d)" + ita "Impossibile leggere lo stato di '%-.200s' (errno: %d)" + jpn "'%-.200s' ¤Î¥¹¥Æ¥¤¥¿¥¹¤¬ÆÀ¤é¤ì¤Þ¤»¤ó. (errno: %d)" + kor "'%-.200s'ÀÇ »óŸ¦ ¾òÁö ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)" + nor "Kan ikke lese statusen til '%-.200s' (Feilkode: %d)" + norwegian-ny "Kan ikkje lese statusen til '%-.200s' (Feilkode: %d)" + pol "Nie mo¿na otrzymaæ statusu '%-.200s' (Kod b³êdu: %d)" + por "Não pode obter o status de '%-.200s' (erro no. %d)" + rum "Nu pot sa obtin statusul lui '%-.200s' (Eroare: %d)" + rus "îÅ×ÏÚÍÏÖÎÏ ÐÏÌÕÞÉÔØ ÓÔÁÔÕÓÎÕÀ ÉÎÆÏÒÍÁÃÉÀ Ï '%-.200s' (ÏÛÉÂËÁ: %d)" + serbian "Ne mogu da dobijem stanje file-a '%-.200s' (errno: %d)" + slo "Nemô¾em zisti» stav '%-.200s' (chybový kód: %d)" + spa "No puedo obtener el estado de '%-.200s' (Error: %d)" + swe "Kan inte läsa filinformationen (stat) från '%-.200s' (Felkod: %d)" + ukr "îÅ ÍÏÖÕ ÏÔÒÉÍÁÔÉ ÓÔÁÔÕÓ '%-.200s' (ÐÏÍÉÌËÁ: %d)" ER_CANT_GET_WD cze "Chyba p-Bøi zji¹»ování pracovní adresáø (chybový kód: %d)" dan "Kan ikke læse aktive folder (Fejlkode: %d)" @@ -350,55 +350,55 @@ ER_CANT_LOCK swe "Kan inte låsa filen. (Felkod: %d)" ukr "îÅ ÍÏÖÕ ÚÁÂÌÏËÕ×ÁÔÉ ÆÁÊÌ (ÐÏÍÉÌËÁ: %d)" ER_CANT_OPEN_FILE - cze "Nemohu otev-Bøít soubor '%-.64s' (chybový kód: %d)" - dan "Kan ikke åbne fil: '%-.64s' (Fejlkode: %d)" - nla "Kan de file '%-.64s' niet openen (Errcode: %d)" + cze "Nemohu otev-Bøít soubor '%-.200s' (chybový kód: %d)" + dan "Kan ikke åbne fil: '%-.200s' (Fejlkode: %d)" + nla "Kan de file '%-.200s' niet openen (Errcode: %d)" eng "Can't open file: '%-.200s' (errno: %d)" - jps "'%-.64s' ƒtƒ@ƒCƒ‹‚ðŠJ‚Ž–‚ª‚Å‚«‚Ü‚¹‚ñ (errno: %d)", - est "Ei suuda avada faili '%-.64s' (veakood: %d)" - fre "Ne peut ouvrir le fichier: '%-.64s' (Errcode: %d)" - ger "Kann Datei '%-.64s' nicht öffnen (Fehler: %d)" - greek "Äåí åßíáé äõíáôü íá áíïé÷ôåß ôï áñ÷åßï: '%-.64s' (êùäéêüò ëÜèïõò: %d)" - hun "A '%-.64s' file nem nyithato meg (hibakod: %d)" - ita "Impossibile aprire il file: '%-.64s' (errno: %d)" - jpn "'%-.64s' ¥Õ¥¡¥¤¥ë¤ò³«¤¯»ö¤¬¤Ç¤¤Þ¤»¤ó (errno: %d)" - kor "ÈÀÏÀ» ¿Áö ¸øÇß½À´Ï´Ù.: '%-.64s' (¿¡·¯¹øÈ£: %d)" - nor "Kan ikke åpne fila: '%-.64s' (Feilkode: %d)" - norwegian-ny "Kan ikkje åpne fila: '%-.64s' (Feilkode: %d)" - pol "Nie mo¿na otworzyæ pliku: '%-.64s' (Kod b³êdu: %d)" - por "Não pode abrir o arquivo '%-.64s' (erro no. %d)" - rum "Nu pot sa deschid fisierul: '%-.64s' (Eroare: %d)" - rus "îÅ×ÏÚÍÏÖÎÏ ÏÔËÒÙÔØ ÆÁÊÌ: '%-.64s' (ÏÛÉÂËÁ: %d)" - serbian "Ne mogu da otvorim file: '%-.64s' (errno: %d)" - slo "Nemô¾em otvori» súbor: '%-.64s' (chybový kód: %d)" - spa "No puedo abrir archivo: '%-.64s' (Error: %d)" - swe "Kan inte använda '%-.64s' (Felkod: %d)" - ukr "îÅ ÍÏÖÕ ×¦ÄËÒÉÔÉ ÆÁÊÌ: '%-.64s' (ÐÏÍÉÌËÁ: %d)" + jps "'%-.200s' ƒtƒ@ƒCƒ‹‚ðŠJ‚Ž–‚ª‚Å‚«‚Ü‚¹‚ñ (errno: %d)", + est "Ei suuda avada faili '%-.200s' (veakood: %d)" + fre "Ne peut ouvrir le fichier: '%-.200s' (Errcode: %d)" + ger "Kann Datei '%-.200s' nicht öffnen (Fehler: %d)" + greek "Äåí åßíáé äõíáôü íá áíïé÷ôåß ôï áñ÷åßï: '%-.200s' (êùäéêüò ëÜèïõò: %d)" + hun "A '%-.200s' file nem nyithato meg (hibakod: %d)" + ita "Impossibile aprire il file: '%-.200s' (errno: %d)" + jpn "'%-.200s' ¥Õ¥¡¥¤¥ë¤ò³«¤¯»ö¤¬¤Ç¤¤Þ¤»¤ó (errno: %d)" + kor "ÈÀÏÀ» ¿Áö ¸øÇß½À´Ï´Ù.: '%-.200s' (¿¡·¯¹øÈ£: %d)" + nor "Kan ikke åpne fila: '%-.200s' (Feilkode: %d)" + norwegian-ny "Kan ikkje åpne fila: '%-.200s' (Feilkode: %d)" + pol "Nie mo¿na otworzyæ pliku: '%-.200s' (Kod b³êdu: %d)" + por "Não pode abrir o arquivo '%-.200s' (erro no. %d)" + rum "Nu pot sa deschid fisierul: '%-.200s' (Eroare: %d)" + rus "îÅ×ÏÚÍÏÖÎÏ ÏÔËÒÙÔØ ÆÁÊÌ: '%-.200s' (ÏÛÉÂËÁ: %d)" + serbian "Ne mogu da otvorim file: '%-.200s' (errno: %d)" + slo "Nemô¾em otvori» súbor: '%-.200s' (chybový kód: %d)" + spa "No puedo abrir archivo: '%-.200s' (Error: %d)" + swe "Kan inte använda '%-.200s' (Felkod: %d)" + ukr "îÅ ÍÏÖÕ ×¦ÄËÒÉÔÉ ÆÁÊÌ: '%-.200s' (ÐÏÍÉÌËÁ: %d)" ER_FILE_NOT_FOUND - cze "Nemohu naj-Bít soubor '%-.64s' (chybový kód: %d)" - dan "Kan ikke finde fila: '%-.64s' (Fejlkode: %d)" - nla "Kan de file: '%-.64s' niet vinden (Errcode: %d)" + cze "Nemohu naj-Bít soubor '%-.200s' (chybový kód: %d)" + dan "Kan ikke finde fila: '%-.200s' (Fejlkode: %d)" + nla "Kan de file: '%-.200s' niet vinden (Errcode: %d)" eng "Can't find file: '%-.200s' (errno: %d)" - jps "'%-.64s' ƒtƒ@ƒCƒ‹‚ðŒ©•t‚¯‚鎖‚ª‚Å‚«‚Ü‚¹‚ñ.(errno: %d)", - est "Ei suuda leida faili '%-.64s' (veakood: %d)" - fre "Ne peut trouver le fichier: '%-.64s' (Errcode: %d)" - ger "Kann Datei '%-.64s' nicht finden (Fehler: %d)" - greek "Äåí âñÝèçêå ôï áñ÷åßï: '%-.64s' (êùäéêüò ëÜèïõò: %d)" - hun "A(z) '%-.64s' file nem talalhato (hibakod: %d)" - ita "Impossibile trovare il file: '%-.64s' (errno: %d)" - jpn "'%-.64s' ¥Õ¥¡¥¤¥ë¤ò¸«ÉÕ¤±¤ë»ö¤¬¤Ç¤¤Þ¤»¤ó.(errno: %d)" - kor "ÈÀÏÀ» ãÁö ¸øÇß½À´Ï´Ù.: '%-.64s' (¿¡·¯¹øÈ£: %d)" - nor "Kan ikke finne fila: '%-.64s' (Feilkode: %d)" - norwegian-ny "Kan ikkje finne fila: '%-.64s' (Feilkode: %d)" - pol "Nie mo¿na znale¥æ pliku: '%-.64s' (Kod b³êdu: %d)" - por "Não pode encontrar o arquivo '%-.64s' (erro no. %d)" - rum "Nu pot sa gasesc fisierul: '%-.64s' (Eroare: %d)" - rus "îÅ×ÏÚÍÏÖÎÏ ÎÁÊÔÉ ÆÁÊÌ: '%-.64s' (ÏÛÉÂËÁ: %d)" - serbian "Ne mogu da pronaðem file: '%-.64s' (errno: %d)" - slo "Nemô¾em nájs» súbor: '%-.64s' (chybový kód: %d)" - spa "No puedo encontrar archivo: '%-.64s' (Error: %d)" - swe "Hittar inte filen '%-.64s' (Felkod: %d)" - ukr "îÅ ÍÏÖÕ ÚÎÁÊÔÉ ÆÁÊÌ: '%-.64s' (ÐÏÍÉÌËÁ: %d)" + jps "'%-.200s' ƒtƒ@ƒCƒ‹‚ðŒ©•t‚¯‚鎖‚ª‚Å‚«‚Ü‚¹‚ñ.(errno: %d)", + est "Ei suuda leida faili '%-.200s' (veakood: %d)" + fre "Ne peut trouver le fichier: '%-.200s' (Errcode: %d)" + ger "Kann Datei '%-.200s' nicht finden (Fehler: %d)" + greek "Äåí âñÝèçêå ôï áñ÷åßï: '%-.200s' (êùäéêüò ëÜèïõò: %d)" + hun "A(z) '%-.200s' file nem talalhato (hibakod: %d)" + ita "Impossibile trovare il file: '%-.200s' (errno: %d)" + jpn "'%-.200s' ¥Õ¥¡¥¤¥ë¤ò¸«ÉÕ¤±¤ë»ö¤¬¤Ç¤¤Þ¤»¤ó.(errno: %d)" + kor "ÈÀÏÀ» ãÁö ¸øÇß½À´Ï´Ù.: '%-.200s' (¿¡·¯¹øÈ£: %d)" + nor "Kan ikke finne fila: '%-.200s' (Feilkode: %d)" + norwegian-ny "Kan ikkje finne fila: '%-.200s' (Feilkode: %d)" + pol "Nie mo¿na znale¥æ pliku: '%-.200s' (Kod b³êdu: %d)" + por "Não pode encontrar o arquivo '%-.200s' (erro no. %d)" + rum "Nu pot sa gasesc fisierul: '%-.200s' (Eroare: %d)" + rus "îÅ×ÏÚÍÏÖÎÏ ÎÁÊÔÉ ÆÁÊÌ: '%-.200s' (ÏÛÉÂËÁ: %d)" + serbian "Ne mogu da pronaðem file: '%-.200s' (errno: %d)" + slo "Nemô¾em nájs» súbor: '%-.200s' (chybový kód: %d)" + spa "No puedo encontrar archivo: '%-.200s' (Error: %d)" + swe "Hittar inte filen '%-.200s' (Felkod: %d)" + ukr "îÅ ÍÏÖÕ ÚÎÁÊÔÉ ÆÁÊÌ: '%-.200s' (ÐÏÍÉÌËÁ: %d)" ER_CANT_READ_DIR cze "Nemohu -Bèíst adresáø '%-.64s' (chybový kód: %d)" dan "Kan ikke læse folder '%-.64s' (Fejlkode: %d)" @@ -480,7 +480,7 @@ ER_DISK_FULL jps "Disk full (%s). ’N‚©‚ª‰½‚©‚ðŒ¸‚ç‚·‚Ü‚Å‚Ü‚Á‚Ä‚‚¾‚³‚¢...", est "Ketas täis (%s). Ootame kuni tekib vaba ruumi..." fre "Disque plein (%s). J'attend que quelqu'un libère de l'espace..." - ger "Festplatte voll (%-.64s). Warte, bis jemand Platz schafft ..." + ger "Festplatte voll (%s). Warte, bis jemand Platz schafft ..." greek "Äåí õðÜñ÷åé ÷þñïò óôï äßóêï (%s). Ðáñáêáëþ, ðåñéìÝíåôå íá åëåõèåñùèåß ÷þñïò..." hun "A lemez megtelt (%s)." ita "Disco pieno (%s). In attesa che qualcuno liberi un po' di spazio..." @@ -546,80 +546,80 @@ ER_ERROR_ON_CLOSE swe "Fick fel vid stängning av '%-.64s' (Felkod: %d)" ukr "îÅ ÍÏÖÕ ÚÁËÒÉÔÉ '%-.64s' (ÐÏÍÉÌËÁ: %d)" ER_ERROR_ON_READ - cze "Chyba p-Bøi ètení souboru '%-.64s' (chybový kód: %d)" - dan "Fejl ved læsning af '%-.64s' (Fejlkode: %d)" - nla "Fout bij het lezen van file '%-.64s' (Errcode: %d)" + cze "Chyba p-Bøi ètení souboru '%-.200s' (chybový kód: %d)" + dan "Fejl ved læsning af '%-.200s' (Fejlkode: %d)" + nla "Fout bij het lezen van file '%-.200s' (Errcode: %d)" eng "Error reading file '%-.200s' (errno: %d)" - jps "'%-.64s' ƒtƒ@ƒCƒ‹‚Ì“Ç‚Ýž‚݃Gƒ‰[ (errno: %d)", - est "Viga faili '%-.64s' lugemisel (veakood: %d)" - fre "Erreur en lecture du fichier '%-.64s' (Errcode: %d)" - ger "Fehler beim Lesen der Datei '%-.64s' (Fehler: %d)" - greek "Ðñüâëçìá êáôÜ ôçí áíÜãíùóç ôïõ áñ÷åßïõ '%-.64s' (êùäéêüò ëÜèïõò: %d)" - hun "Hiba a '%-.64s'file olvasasakor. (hibakod: %d)" - ita "Errore durante la lettura del file '%-.64s' (errno: %d)" - jpn "'%-.64s' ¥Õ¥¡¥¤¥ë¤ÎÆɤ߹þ¤ß¥¨¥é¡¼ (errno: %d)" - kor "'%-.64s'ÈÀÏ Àб⠿¡·¯ (¿¡·¯¹øÈ£: %d)" - nor "Feil ved lesing av '%-.64s' (Feilkode: %d)" - norwegian-ny "Feil ved lesing av '%-.64s' (Feilkode: %d)" - pol "B³?d podczas odczytu pliku '%-.64s' (Kod b³êdu: %d)" - por "Erro ao ler arquivo '%-.64s' (erro no. %d)" - rum "Eroare citind fisierul '%-.64s' (errno: %d)" - rus "ïÛÉÂËÁ ÞÔÅÎÉÑ ÆÁÊÌÁ '%-.64s' (ÏÛÉÂËÁ: %d)" - serbian "Greška pri èitanju file-a '%-.64s' (errno: %d)" - slo "Chyba pri èítaní súboru '%-.64s' (chybový kód: %d)" - spa "Error leyendo el fichero '%-.64s' (Error: %d)" - swe "Fick fel vid läsning av '%-.64s' (Felkod %d)" - ukr "îÅ ÍÏÖÕ ÐÒÏÞÉÔÁÔÉ ÆÁÊÌ '%-.64s' (ÐÏÍÉÌËÁ: %d)" + jps "'%-.200s' ƒtƒ@ƒCƒ‹‚Ì“Ç‚Ýž‚݃Gƒ‰[ (errno: %d)", + est "Viga faili '%-.200s' lugemisel (veakood: %d)" + fre "Erreur en lecture du fichier '%-.200s' (Errcode: %d)" + ger "Fehler beim Lesen der Datei '%-.200s' (Fehler: %d)" + greek "Ðñüâëçìá êáôÜ ôçí áíÜãíùóç ôïõ áñ÷åßïõ '%-.200s' (êùäéêüò ëÜèïõò: %d)" + hun "Hiba a '%-.200s'file olvasasakor. (hibakod: %d)" + ita "Errore durante la lettura del file '%-.200s' (errno: %d)" + jpn "'%-.200s' ¥Õ¥¡¥¤¥ë¤ÎÆɤ߹þ¤ß¥¨¥é¡¼ (errno: %d)" + kor "'%-.200s'ÈÀÏ Àб⠿¡·¯ (¿¡·¯¹øÈ£: %d)" + nor "Feil ved lesing av '%-.200s' (Feilkode: %d)" + norwegian-ny "Feil ved lesing av '%-.200s' (Feilkode: %d)" + pol "B³?d podczas odczytu pliku '%-.200s' (Kod b³êdu: %d)" + por "Erro ao ler arquivo '%-.200s' (erro no. %d)" + rum "Eroare citind fisierul '%-.200s' (errno: %d)" + rus "ïÛÉÂËÁ ÞÔÅÎÉÑ ÆÁÊÌÁ '%-.200s' (ÏÛÉÂËÁ: %d)" + serbian "Greška pri èitanju file-a '%-.200s' (errno: %d)" + slo "Chyba pri èítaní súboru '%-.200s' (chybový kód: %d)" + spa "Error leyendo el fichero '%-.200s' (Error: %d)" + swe "Fick fel vid läsning av '%-.200s' (Felkod %d)" + ukr "îÅ ÍÏÖÕ ÐÒÏÞÉÔÁÔÉ ÆÁÊÌ '%-.200s' (ÐÏÍÉÌËÁ: %d)" ER_ERROR_ON_RENAME - cze "Chyba p-Bøi pøejmenování '%-.64s' na '%-.64s' (chybový kód: %d)" - dan "Fejl ved omdøbning af '%-.64s' til '%-.64s' (Fejlkode: %d)" - nla "Fout bij het hernoemen van '%-.64s' naar '%-.64s' (Errcode: %d)" - eng "Error on rename of '%-.64s' to '%-.64s' (errno: %d)" - jps "'%-.64s' ‚ð '%-.64s' ‚É rename ‚Å‚«‚Ü‚¹‚ñ (errno: %d)", - est "Viga faili '%-.64s' ümbernimetamisel '%-.64s'-ks (veakood: %d)" - fre "Erreur en renommant '%-.64s' en '%-.64s' (Errcode: %d)" - ger "Fehler beim Umbenennen von '%-.64s' in '%-.64s' (Fehler: %d)" - greek "Ðñüâëçìá êáôÜ ôçí ìåôïíïìáóßá ôïõ áñ÷åßïõ '%-.64s' to '%-.64s' (êùäéêüò ëÜèïõò: %d)" - hun "Hiba a '%-.64s' file atnevezesekor. (hibakod: %d)" - ita "Errore durante la rinominazione da '%-.64s' a '%-.64s' (errno: %d)" - jpn "'%-.64s' ¤ò '%-.64s' ¤Ë rename ¤Ç¤¤Þ¤»¤ó (errno: %d)" - kor "'%-.64s'¸¦ '%-.64s'·Î À̸§ º¯°æÁß ¿¡·¯ (¿¡·¯¹øÈ£: %d)" - nor "Feil ved omdøping av '%-.64s' til '%-.64s' (Feilkode: %d)" - norwegian-ny "Feil ved omdøyping av '%-.64s' til '%-.64s' (Feilkode: %d)" - pol "B³?d podczas zmieniania nazwy '%-.64s' na '%-.64s' (Kod b³êdu: %d)" - por "Erro ao renomear '%-.64s' para '%-.64s' (erro no. %d)" - rum "Eroare incercind sa renumesc '%-.64s' in '%-.64s' (errno: %d)" - rus "ïÛÉÂËÁ ÐÒÉ ÐÅÒÅÉÍÅÎÏ×ÁÎÉÉ '%-.64s' × '%-.64s' (ÏÛÉÂËÁ: %d)" - serbian "Greška pri promeni imena '%-.64s' na '%-.64s' (errno: %d)" - slo "Chyba pri premenovávaní '%-.64s' na '%-.64s' (chybový kód: %d)" - spa "Error en el renombrado de '%-.64s' a '%-.64s' (Error: %d)" - swe "Kan inte byta namn från '%-.64s' till '%-.64s' (Felkod: %d)" - ukr "îÅ ÍÏÖÕ ÐÅÒÅÊÍÅÎÕ×ÁÔÉ '%-.64s' Õ '%-.64s' (ÐÏÍÉÌËÁ: %d)" + cze "Chyba p-Bøi pøejmenování '%-.150s' na '%-.150s' (chybový kód: %d)" + dan "Fejl ved omdøbning af '%-.150s' til '%-.150s' (Fejlkode: %d)" + nla "Fout bij het hernoemen van '%-.150s' naar '%-.150s' (Errcode: %d)" + eng "Error on rename of '%-.150s' to '%-.150s' (errno: %d)" + jps "'%-.150s' ‚ð '%-.150s' ‚É rename ‚Å‚«‚Ü‚¹‚ñ (errno: %d)", + est "Viga faili '%-.150s' ümbernimetamisel '%-.150s'-ks (veakood: %d)" + fre "Erreur en renommant '%-.150s' en '%-.150s' (Errcode: %d)" + ger "Fehler beim Umbenennen von '%-.150s' in '%-.150s' (Fehler: %d)" + greek "Ðñüâëçìá êáôÜ ôçí ìåôïíïìáóßá ôïõ áñ÷åßïõ '%-.150s' to '%-.150s' (êùäéêüò ëÜèïõò: %d)" + hun "Hiba a '%-.150s' file atnevezesekor '%-.150s'. (hibakod: %d)" + ita "Errore durante la rinominazione da '%-.150s' a '%-.150s' (errno: %d)" + jpn "'%-.150s' ¤ò '%-.150s' ¤Ë rename ¤Ç¤¤Þ¤»¤ó (errno: %d)" + kor "'%-.150s'¸¦ '%-.150s'·Î À̸§ º¯°æÁß ¿¡·¯ (¿¡·¯¹øÈ£: %d)" + nor "Feil ved omdøping av '%-.150s' til '%-.150s' (Feilkode: %d)" + norwegian-ny "Feil ved omdøyping av '%-.150s' til '%-.150s' (Feilkode: %d)" + pol "B³?d podczas zmieniania nazwy '%-.150s' na '%-.150s' (Kod b³êdu: %d)" + por "Erro ao renomear '%-.150s' para '%-.150s' (erro no. %d)" + rum "Eroare incercind sa renumesc '%-.150s' in '%-.150s' (errno: %d)" + rus "ïÛÉÂËÁ ÐÒÉ ÐÅÒÅÉÍÅÎÏ×ÁÎÉÉ '%-.150s' × '%-.150s' (ÏÛÉÂËÁ: %d)" + serbian "Greška pri promeni imena '%-.150s' na '%-.150s' (errno: %d)" + slo "Chyba pri premenovávaní '%-.150s' na '%-.150s' (chybový kód: %d)" + spa "Error en el renombrado de '%-.150s' a '%-.150s' (Error: %d)" + swe "Kan inte byta namn från '%-.150s' till '%-.150s' (Felkod: %d)" + ukr "îÅ ÍÏÖÕ ÐÅÒÅÊÍÅÎÕ×ÁÔÉ '%-.150s' Õ '%-.150s' (ÐÏÍÉÌËÁ: %d)" ER_ERROR_ON_WRITE - cze "Chyba p-Bøi zápisu do souboru '%-.64s' (chybový kód: %d)" - dan "Fejl ved skriving av filen '%-.64s' (Fejlkode: %d)" - nla "Fout bij het wegschrijven van file '%-.64s' (Errcode: %d)" + cze "Chyba p-Bøi zápisu do souboru '%-.200s' (chybový kód: %d)" + dan "Fejl ved skriving av filen '%-.200s' (Fejlkode: %d)" + nla "Fout bij het wegschrijven van file '%-.200s' (Errcode: %d)" eng "Error writing file '%-.200s' (errno: %d)" - jps "'%-.64s' ƒtƒ@ƒCƒ‹‚ð‘‚Ž–‚ª‚Å‚«‚Ü‚¹‚ñ (errno: %d)", - est "Viga faili '%-.64s' kirjutamisel (veakood: %d)" - fre "Erreur d'écriture du fichier '%-.64s' (Errcode: %d)" - ger "Fehler beim Speichern der Datei '%-.64s' (Fehler: %d)" - greek "Ðñüâëçìá êáôÜ ôçí áðïèÞêåõóç ôïõ áñ÷åßïõ '%-.64s' (êùäéêüò ëÜèïõò: %d)" - hun "Hiba a '%-.64s' file irasakor. (hibakod: %d)" - ita "Errore durante la scrittura del file '%-.64s' (errno: %d)" - jpn "'%-.64s' ¥Õ¥¡¥¤¥ë¤ò½ñ¤¯»ö¤¬¤Ç¤¤Þ¤»¤ó (errno: %d)" - kor "'%-.64s'ÈÀÏ ±â·Ï Áß ¿¡·¯ (¿¡·¯¹øÈ£: %d)" - nor "Feil ved skriving av fila '%-.64s' (Feilkode: %d)" - norwegian-ny "Feil ved skriving av fila '%-.64s' (Feilkode: %d)" - pol "B³?d podczas zapisywania pliku '%-.64s' (Kod b³êdu: %d)" - por "Erro ao gravar arquivo '%-.64s' (erro no. %d)" - rum "Eroare scriind fisierul '%-.64s' (errno: %d)" - rus "ïÛÉÂËÁ ÚÁÐÉÓÉ × ÆÁÊÌ '%-.64s' (ÏÛÉÂËÁ: %d)" - serbian "Greška pri upisu '%-.64s' (errno: %d)" - slo "Chyba pri zápise do súboru '%-.64s' (chybový kód: %d)" - spa "Error escribiendo el archivo '%-.64s' (Error: %d)" - swe "Fick fel vid skrivning till '%-.64s' (Felkod %d)" - ukr "îÅ ÍÏÖÕ ÚÁÐÉÓÁÔÉ ÆÁÊÌ '%-.64s' (ÐÏÍÉÌËÁ: %d)" + jps "'%-.200s' ƒtƒ@ƒCƒ‹‚ð‘‚Ž–‚ª‚Å‚«‚Ü‚¹‚ñ (errno: %d)", + est "Viga faili '%-.200s' kirjutamisel (veakood: %d)" + fre "Erreur d'écriture du fichier '%-.200s' (Errcode: %d)" + ger "Fehler beim Speichern der Datei '%-.200s' (Fehler: %d)" + greek "Ðñüâëçìá êáôÜ ôçí áðïèÞêåõóç ôïõ áñ÷åßïõ '%-.200s' (êùäéêüò ëÜèïõò: %d)" + hun "Hiba a '%-.200s' file irasakor. (hibakod: %d)" + ita "Errore durante la scrittura del file '%-.200s' (errno: %d)" + jpn "'%-.200s' ¥Õ¥¡¥¤¥ë¤ò½ñ¤¯»ö¤¬¤Ç¤¤Þ¤»¤ó (errno: %d)" + kor "'%-.200s'ÈÀÏ ±â·Ï Áß ¿¡·¯ (¿¡·¯¹øÈ£: %d)" + nor "Feil ved skriving av fila '%-.200s' (Feilkode: %d)" + norwegian-ny "Feil ved skriving av fila '%-.200s' (Feilkode: %d)" + pol "B³?d podczas zapisywania pliku '%-.200s' (Kod b³êdu: %d)" + por "Erro ao gravar arquivo '%-.200s' (erro no. %d)" + rum "Eroare scriind fisierul '%-.200s' (errno: %d)" + rus "ïÛÉÂËÁ ÚÁÐÉÓÉ × ÆÁÊÌ '%-.200s' (ÏÛÉÂËÁ: %d)" + serbian "Greška pri upisu '%-.200s' (errno: %d)" + slo "Chyba pri zápise do súboru '%-.200s' (chybový kód: %d)" + spa "Error escribiendo el archivo '%-.200s' (Error: %d)" + swe "Fick fel vid skrivning till '%-.200s' (Felkod %d)" + ukr "îÅ ÍÏÖÕ ÚÁÐÉÓÁÔÉ ÆÁÊÌ '%-.200s' (ÐÏÍÉÌËÁ: %d)" ER_FILE_USED cze "'%-.64s' je zam-Bèen proti zmìnám" dan "'%-.64s' er låst mod opdateringer" @@ -741,7 +741,7 @@ ER_ILLEGAL_HA serbian "Handler tabela za '%-.64s' nema ovu opciju" slo "Obsluha tabuµky '%-.64s' nemá tento parameter" spa "El manejador de la tabla de '%-.64s' no tiene esta opcion" - swe "Registrets databas har inte denna facilitet" + swe "Tabellhanteraren for tabell '%-.64s' stödjer ej detta" ukr "äÅÓËÒÉÐÔÏÒ ÔÁÂÌÉæ '%-.64s' ÎÅ ÍÁ¤ 椧 ×ÌÁÓÔÉ×ÏÓÔ¦" ER_KEY_NOT_FOUND cze "Nemohu naj-Bít záznam v '%-.64s'" @@ -766,58 +766,58 @@ ER_KEY_NOT_FOUND serbian "Ne mogu da pronaðem slog u '%-.64s'" slo "Nemô¾em nájs» záznam v '%-.64s'" spa "No puedo encontrar el registro en '%-.64s'" - swe "Hittar inte posten" + swe "Hittar inte posten '%-.64s'" ukr "îÅ ÍÏÖÕ ÚÁÐÉÓÁÔÉ Õ '%-.64s'" ER_NOT_FORM_FILE - cze "Nespr-Bávná informace v souboru '%-.64s'" - dan "Forkert indhold i: '%-.64s'" - nla "Verkeerde info in file: '%-.64s'" + cze "Nespr-Bávná informace v souboru '%-.200s'" + dan "Forkert indhold i: '%-.200s'" + nla "Verkeerde info in file: '%-.200s'" eng "Incorrect information in file: '%-.200s'" - jps "ƒtƒ@ƒCƒ‹ '%-.64s' ‚Ì info ‚ªŠÔˆá‚Á‚Ä‚¢‚é‚悤‚Å‚·", - est "Vigane informatsioon failis '%-.64s'" - fre "Information erronnée dans le fichier: '%-.64s'" - ger "Falsche Information in Datei '%-.64s'" - greek "ËÜèïò ðëçñïöïñßåò óôï áñ÷åßï: '%-.64s'" - hun "Ervenytelen info a file-ban: '%-.64s'" - ita "Informazione errata nel file: '%-.64s'" - jpn "¥Õ¥¡¥¤¥ë '%-.64s' ¤Î info ¤¬´Ö°ã¤Ã¤Æ¤¤¤ë¤è¤¦¤Ç¤¹" - kor "ÈÀÏÀÇ ºÎÁ¤È®ÇÑ Á¤º¸: '%-.64s'" - nor "Feil informasjon i filen: '%-.64s'" - norwegian-ny "Feil informasjon i fila: '%-.64s'" - pol "Niew³a?ciwa informacja w pliku: '%-.64s'" - por "Informação incorreta no arquivo '%-.64s'" - rum "Informatie incorecta in fisierul: '%-.64s'" - rus "îÅËÏÒÒÅËÔÎÁÑ ÉÎÆÏÒÍÁÃÉÑ × ÆÁÊÌÅ '%-.64s'" - serbian "Pogrešna informacija u file-u: '%-.64s'" - slo "Nesprávna informácia v súbore: '%-.64s'" - spa "Informacion erronea en el archivo: '%-.64s'" - swe "Felaktig fil: '%-.64s'" - ukr "èÉÂÎÁ ¦ÎÆÏÒÍÁÃ¦Ñ Õ ÆÁÊ̦: '%-.64s'" + jps "ƒtƒ@ƒCƒ‹ '%-.200s' ‚Ì info ‚ªŠÔˆá‚Á‚Ä‚¢‚é‚悤‚Å‚·", + est "Vigane informatsioon failis '%-.200s'" + fre "Information erronnée dans le fichier: '%-.200s'" + ger "Falsche Information in Datei '%-.200s'" + greek "ËÜèïò ðëçñïöïñßåò óôï áñ÷åßï: '%-.200s'" + hun "Ervenytelen info a file-ban: '%-.200s'" + ita "Informazione errata nel file: '%-.200s'" + jpn "¥Õ¥¡¥¤¥ë '%-.200s' ¤Î info ¤¬´Ö°ã¤Ã¤Æ¤¤¤ë¤è¤¦¤Ç¤¹" + kor "ÈÀÏÀÇ ºÎÁ¤È®ÇÑ Á¤º¸: '%-.200s'" + nor "Feil informasjon i filen: '%-.200s'" + norwegian-ny "Feil informasjon i fila: '%-.200s'" + pol "Niew³a?ciwa informacja w pliku: '%-.200s'" + por "Informação incorreta no arquivo '%-.200s'" + rum "Informatie incorecta in fisierul: '%-.200s'" + rus "îÅËÏÒÒÅËÔÎÁÑ ÉÎÆÏÒÍÁÃÉÑ × ÆÁÊÌÅ '%-.200s'" + serbian "Pogrešna informacija u file-u: '%-.200s'" + slo "Nesprávna informácia v súbore: '%-.200s'" + spa "Informacion erronea en el archivo: '%-.200s'" + swe "Felaktig fil: '%-.200s'" + ukr "èÉÂÎÁ ¦ÎÆÏÒÍÁÃ¦Ñ Õ ÆÁÊ̦: '%-.200s'" ER_NOT_KEYFILE - cze "Nespr-Bávný klíè pro tabulku '%-.64s'; pokuste se ho opravit" - dan "Fejl i indeksfilen til tabellen '%-.64s'; prøv at reparere den" - nla "Verkeerde zoeksleutel file voor tabel: '%-.64s'; probeer het te repareren" + cze "Nespr-Bávný klíè pro tabulku '%-.200s'; pokuste se ho opravit" + dan "Fejl i indeksfilen til tabellen '%-.200s'; prøv at reparere den" + nla "Verkeerde zoeksleutel file voor tabel: '%-.200s'; probeer het te repareren" eng "Incorrect key file for table '%-.200s'; try to repair it" - jps "'%-.64s' ƒe[ƒuƒ‹‚Ì key file ‚ªŠÔˆá‚Á‚Ä‚¢‚é‚悤‚Å‚·. C•œ‚ð‚µ‚Ä‚‚¾‚³‚¢", - est "Tabeli '%-.64s' võtmefail on vigane; proovi seda parandada" - fre "Index corrompu dans la table: '%-.64s'; essayez de le réparer" - ger "Fehlerhafte Index-Datei für Tabelle '%-.64s'; versuche zu reparieren" - greek "ËÜèïò áñ÷åßï ôáîéíüìéóçò (key file) ãéá ôïí ðßíáêá: '%-.64s'; Ðáñáêáëþ, äéïñèþóôå ôï!" - hun "Ervenytelen kulcsfile a tablahoz: '%-.64s'; probalja kijavitani!" - ita "File chiave errato per la tabella : '%-.64s'; prova a riparalo" - jpn "'%-.64s' ¥Æ¡¼¥Ö¥ë¤Î key file ¤¬´Ö°ã¤Ã¤Æ¤¤¤ë¤è¤¦¤Ç¤¹. ½¤Éü¤ò¤·¤Æ¤¯¤À¤µ¤¤" - kor "'%-.64s' Å×À̺íÀÇ ºÎÁ¤È®ÇÑ Å° Á¸Àç. ¼öÁ¤ÇϽÿÀ!" - nor "Tabellen '%-.64s' har feil i nøkkelfilen; forsøk å reparer den" - norwegian-ny "Tabellen '%-.64s' har feil i nykkelfila; prøv å reparere den" - pol "Niew³a?ciwy plik kluczy dla tabeli: '%-.64s'; spróbuj go naprawiæ" - por "Arquivo de índice incorreto para tabela '%-.64s'; tente repará-lo" - rum "Cheia fisierului incorecta pentru tabela: '%-.64s'; incearca s-o repari" - rus "îÅËÏÒÒÅËÔÎÙÊ ÉÎÄÅËÓÎÙÊ ÆÁÊÌ ÄÌÑ ÔÁÂÌÉÃÙ: '%-.64s'. ðÏÐÒÏÂÕÊÔÅ ×ÏÓÓÔÁÎÏ×ÉÔØ ÅÇÏ" - serbian "Pogrešan key file za tabelu: '%-.64s'; probajte da ga ispravite" - slo "Nesprávny kµúè pre tabuµku '%-.64s'; pokúste sa ho opravi»" - spa "Clave de archivo erronea para la tabla: '%-.64s'; intente repararlo" - swe "Fatalt fel vid hantering av register '%-.64s'; kör en reparation" - ukr "èÉÂÎÉÊ ÆÁÊÌ ËÌÀÞÅÊ ÄÌÑ ÔÁÂÌÉæ: '%-.64s'; óÐÒÏÂÕÊÔÅ ÊÏÇÏ ×¦ÄÎÏ×ÉÔÉ" + jps "'%-.200s' ƒe[ƒuƒ‹‚Ì key file ‚ªŠÔˆá‚Á‚Ä‚¢‚é‚悤‚Å‚·. C•œ‚ð‚µ‚Ä‚‚¾‚³‚¢", + est "Tabeli '%-.200s' võtmefail on vigane; proovi seda parandada" + fre "Index corrompu dans la table: '%-.200s'; essayez de le réparer" + ger "Fehlerhafte Index-Datei für Tabelle '%-.200s'; versuche zu reparieren" + greek "ËÜèïò áñ÷åßï ôáîéíüìéóçò (key file) ãéá ôïí ðßíáêá: '%-.200s'; Ðáñáêáëþ, äéïñèþóôå ôï!" + hun "Ervenytelen kulcsfile a tablahoz: '%-.200s'; probalja kijavitani!" + ita "File chiave errato per la tabella : '%-.200s'; prova a riparalo" + jpn "'%-.200s' ¥Æ¡¼¥Ö¥ë¤Î key file ¤¬´Ö°ã¤Ã¤Æ¤¤¤ë¤è¤¦¤Ç¤¹. ½¤Éü¤ò¤·¤Æ¤¯¤À¤µ¤¤" + kor "'%-.200s' Å×À̺íÀÇ ºÎÁ¤È®ÇÑ Å° Á¸Àç. ¼öÁ¤ÇϽÿÀ!" + nor "Tabellen '%-.200s' har feil i nøkkelfilen; forsøk å reparer den" + norwegian-ny "Tabellen '%-.200s' har feil i nykkelfila; prøv å reparere den" + pol "Niew³a?ciwy plik kluczy dla tabeli: '%-.200s'; spróbuj go naprawiæ" + por "Arquivo de índice incorreto para tabela '%-.200s'; tente repará-lo" + rum "Cheia fisierului incorecta pentru tabela: '%-.200s'; incearca s-o repari" + rus "îÅËÏÒÒÅËÔÎÙÊ ÉÎÄÅËÓÎÙÊ ÆÁÊÌ ÄÌÑ ÔÁÂÌÉÃÙ: '%-.200s'. ðÏÐÒÏÂÕÊÔÅ ×ÏÓÓÔÁÎÏ×ÉÔØ ÅÇÏ" + serbian "Pogrešan key file za tabelu: '%-.200s'; probajte da ga ispravite" + slo "Nesprávny kµúè pre tabuµku '%-.200s'; pokúste sa ho opravi»" + spa "Clave de archivo erronea para la tabla: '%-.200s'; intente repararlo" + swe "Fatalt fel vid hantering av register '%-.200s'; kör en reparation" + ukr "èÉÂÎÉÊ ÆÁÊÌ ËÌÀÞÅÊ ÄÌÑ ÔÁÂÌÉæ: '%-.200s'; óÐÒÏÂÕÊÔÅ ÊÏÇÏ ×¦ÄÎÏ×ÉÔÉ" ER_OLD_KEYFILE cze "Star-Bý klíèový soubor pro '%-.64s'; opravte ho." dan "Gammel indeksfil for tabellen '%-.64s'; reparer den" @@ -950,7 +950,7 @@ ER_CON_COUNT_ERROR 08004 eng "Too many connections" jps "Ú‘±‚ª‘½‚·‚¬‚Ü‚·", est "Liiga palju samaaegseid ühendusi" - fre "Trop de connections" + fre "Trop de connexions" ger "Zu viele Verbindungen" greek "ÕðÜñ÷ïõí ðïëëÝò óõíäÝóåéò..." hun "Tul sok kapcsolat" @@ -1072,7 +1072,7 @@ ER_ACCESS_DENIED_ERROR 28000 jps "ƒ†[ƒU[ '%-.32s'@'%-.64s' ‚ð‹‘”Û‚µ‚Ü‚·.uUsing password: %s)", est "Ligipääs keelatud kasutajale '%-.32s'@'%-.64s' (kasutab parooli: %s)" fre "Accès refusé pour l'utilisateur: '%-.32s'@'@%-.64s' (mot de passe: %s)" - ger "Benutzer '%-.32s'@'%-.64s' hat keine Zugriffsberechtigung (verwendetes Passwort: %-.64s)" + ger "Benutzer '%-.32s'@'%-.64s' hat keine Zugriffsberechtigung (verwendetes Passwort: %s)" greek "Äåí åðéôÝñåôáé ç ðñüóâáóç óôï ÷ñÞóôç: '%-.32s'@'%-.64s' (÷ñÞóç password: %s)" hun "A(z) '%-.32s'@'%-.64s' felhasznalo szamara tiltott eleres. (Hasznalja a jelszot: %s)" ita "Accesso non consentito per l'utente: '%-.32s'@'%-.64s' (Password: %s)" @@ -1239,28 +1239,28 @@ ER_BAD_TABLE_ERROR 42S02 swe "Okänd tabell '%-.100s'" ukr "îÅצÄÏÍÁ ÔÁÂÌÉÃÑ '%-.100s'" ER_NON_UNIQ_ERROR 23000 - cze "Sloupec '%-.64s' v %s nen-Bí zcela jasný" - dan "Felt: '%-.64s' i tabel %s er ikke entydigt" - nla "Kolom: '%-.64s' in %s is niet eenduidig" + cze "Sloupec '%-.64s' v %-.64s nen-Bí zcela jasný" + dan "Felt: '%-.64s' i tabel %-.64s er ikke entydigt" + nla "Kolom: '%-.64s' in %-.64s is niet eenduidig" eng "Column '%-.64s' in %-.64s is ambiguous" est "Väli '%-.64s' %-.64s-s ei ole ühene" - fre "Champ: '%-.64s' dans %s est ambigu" + fre "Champ: '%-.64s' dans %-.64s est ambigu" ger "Feld '%-.64s' in %-.64s ist nicht eindeutig" greek "Ôï ðåäßï: '%-.64s' óå %-.64s äåí Ý÷åé êáèïñéóôåß" hun "A(z) '%-.64s' oszlop %-.64s-ben ketertelmu" ita "Colonna: '%-.64s' di %-.64s e` ambigua" jpn "Column: '%-.64s' in %-.64s is ambiguous" kor "Ä®·³: '%-.64s' in '%-.64s' ÀÌ ¸ðÈ£ÇÔ" - nor "Felt: '%-.64s' i tabell %s er ikke entydig" - norwegian-ny "Kolonne: '%-.64s' i tabell %s er ikkje eintydig" - pol "Kolumna: '%-.64s' w %s jest dwuznaczna" + nor "Felt: '%-.64s' i tabell %-.64s er ikke entydig" + norwegian-ny "Kolonne: '%-.64s' i tabell %-.64s er ikkje eintydig" + pol "Kolumna: '%-.64s' w %-.64s jest dwuznaczna" por "Coluna '%-.64s' em '%-.64s' é ambígua" rum "Coloana: '%-.64s' in %-.64s este ambigua" rus "óÔÏÌÂÅà '%-.64s' × %-.64s ÚÁÄÁÎ ÎÅÏÄÎÏÚÎÁÞÎÏ" serbian "Kolona '%-.64s' u %-.64s nije jedinstvena u kontekstu" slo "Pole: '%-.64s' v %-.64s je nejasné" - spa "La columna: '%-.64s' en %s es ambigua" - swe "Kolumn '%-.64s' i %s är inte unik" + spa "La columna: '%-.64s' en %-.64s es ambigua" + swe "Kolumn '%-.64s' i %-.64s är inte unik" ukr "óÔÏ×ÂÅÃØ '%-.64s' Õ %-.64s ×ÉÚÎÁÞÅÎÉÊ ÎÅÏÄÎÏÚÎÁÞÎÏ" ER_SERVER_SHUTDOWN 08S01 cze "Prob-Bíhá ukonèování práce serveru" @@ -1288,29 +1288,29 @@ ER_SERVER_SHUTDOWN 08S01 swe "Servern går nu ned" ukr "úÁ×ÅÒÛÕ¤ÔØÓÑ ÒÁÂÏÔÁ ÓÅÒ×ÅÒÁ" ER_BAD_FIELD_ERROR 42S22 S0022 - cze "Nezn-Bámý sloupec '%-.64s' v %s" - dan "Ukendt kolonne '%-.64s' i tabel %s" - nla "Onbekende kolom '%-.64s' in %s" + cze "Nezn-Bámý sloupec '%-.64s' v %-.64s" + dan "Ukendt kolonne '%-.64s' i tabel %-.64s" + nla "Onbekende kolom '%-.64s' in %-.64s" eng "Unknown column '%-.64s' in '%-.64s'" jps "'%-.64s' column ‚Í '%-.64s' ‚É‚Í‚ ‚è‚Ü‚¹‚ñ.", est "Tundmatu tulp '%-.64s' '%-.64s'-s" - fre "Champ '%-.64s' inconnu dans %s" + fre "Champ '%-.64s' inconnu dans %-.64s" ger "Unbekanntes Tabellenfeld '%-.64s' in %-.64s" greek "Áãíùóôï ðåäßï '%-.64s' óå '%-.64s'" hun "A(z) '%-.64s' oszlop ervenytelen '%-.64s'-ben" ita "Colonna sconosciuta '%-.64s' in '%-.64s'" jpn "'%-.64s' column ¤Ï '%-.64s' ¤Ë¤Ï¤¢¤ê¤Þ¤»¤ó." kor "Unknown Ä®·³ '%-.64s' in '%-.64s'" - nor "Ukjent kolonne '%-.64s' i tabell %s" - norwegian-ny "Ukjent felt '%-.64s' i tabell %s" - pol "Nieznana kolumna '%-.64s' w %s" + nor "Ukjent kolonne '%-.64s' i tabell %-.64s" + norwegian-ny "Ukjent felt '%-.64s' i tabell %-.64s" + pol "Nieznana kolumna '%-.64s' w %-.64s" por "Coluna '%-.64s' desconhecida em '%-.64s'" rum "Coloana invalida '%-.64s' in '%-.64s'" rus "îÅÉÚ×ÅÓÔÎÙÊ ÓÔÏÌÂÅà '%-.64s' × '%-.64s'" serbian "Nepoznata kolona '%-.64s' u '%-.64s'" slo "Neznáme pole '%-.64s' v '%-.64s'" - spa "La columna '%-.64s' en %s es desconocida" - swe "Okänd kolumn '%-.64s' i %s" + spa "La columna '%-.64s' en %-.64s es desconocida" + swe "Okänd kolumn '%-.64s' i %-.64s" ukr "îÅצÄÏÍÉÊ ÓÔÏ×ÂÅÃØ '%-.64s' Õ '%-.64s'" ER_WRONG_FIELD_WITH_GROUP 42000 S1009 cze "Pou-B¾ité '%-.64s' nebylo v group by" @@ -1404,29 +1404,29 @@ ER_WRONG_VALUE_COUNT 21S01 swe "Antalet kolumner motsvarar inte antalet värden" ukr "ë¦ÌØ˦ÓÔØ ÓÔÏ×ÂÃ¦× ÎÅ ÓЦ×ÐÁÄÁ¤ Ú Ë¦ÌØ˦ÓÔÀ ÚÎÁÞÅÎØ" ER_TOO_LONG_IDENT 42000 S1009 - cze "Jm-Béno identifikátoru '%-.64s' je pøíli¹ dlouhé" - dan "Navnet '%-.64s' er for langt" - nla "Naam voor herkenning '%-.64s' is te lang" + cze "Jm-Béno identifikátoru '%-.100s' je pøíli¹ dlouhé" + dan "Navnet '%-.100s' er for langt" + nla "Naam voor herkenning '%-.100s' is te lang" eng "Identifier name '%-.100s' is too long" jps "Identifier name '%-.100s' ‚Í’·‚·‚¬‚Ü‚·", est "Identifikaatori '%-.100s' nimi on liiga pikk" - fre "Le nom de l'identificateur '%-.64s' est trop long" + fre "Le nom de l'identificateur '%-.100s' est trop long" ger "Name des Bezeichners '%-.100s' ist zu lang" greek "Ôï identifier name '%-.100s' åßíáé ðïëý ìåãÜëï" hun "A(z) '%-.100s' azonositonev tul hosszu." ita "Il nome dell'identificatore '%-.100s' e` troppo lungo" jpn "Identifier name '%-.100s' ¤ÏŤ¹¤®¤Þ¤¹" kor "Identifier '%-.100s'´Â ³Ê¹« ±æ±º¿ä." - nor "Identifikator '%-.64s' er for lang" - norwegian-ny "Identifikator '%-.64s' er for lang" - pol "Nazwa identyfikatora '%-.64s' jest zbyt d³uga" + nor "Identifikator '%-.100s' er for lang" + norwegian-ny "Identifikator '%-.100s' er for lang" + pol "Nazwa identyfikatora '%-.100s' jest zbyt d³uga" por "Nome identificador '%-.100s' é longo demais" rum "Numele indentificatorului '%-.100s' este prea lung" rus "óÌÉÛËÏÍ ÄÌÉÎÎÙÊ ÉÄÅÎÔÉÆÉËÁÔÏÒ '%-.100s'" serbian "Ime '%-.100s' je predugaèko" slo "Meno identifikátora '%-.100s' je príli¹ dlhé" - spa "El nombre del identificador '%-.64s' es demasiado grande" - swe "Kolumnnamn '%-.64s' är för långt" + spa "El nombre del identificador '%-.100s' es demasiado grande" + swe "Kolumnnamn '%-.100s' är för långt" ukr "¶Í'Ñ ¦ÄÅÎÔÉƦËÁÔÏÒÁ '%-.100s' ÚÁÄÏ×ÇÅ" ER_DUP_FIELDNAME 42S21 S1009 cze "Zdvojen-Bé jméno sloupce '%-.64s'" @@ -1479,30 +1479,30 @@ ER_DUP_KEYNAME 42000 S1009 swe "Nyckelnamn '%-.64s' finns flera gånger" ukr "äÕÂÌÀÀÞÅ ¦Í'Ñ ËÌÀÞÁ '%-.64s'" ER_DUP_ENTRY 23000 S1009 - cze "Zvojen-Bý klíè '%-.64s' (èíslo klíèe '%-.64s')" - dan "Ens værdier '%-.64s' for indeks '%-.64s'" - nla "Dubbele ingang '%-.64s' voor zoeksleutel '%-.64s'" - eng "Duplicate entry '%-.64s' for key '%-.64s'" - jps "'%-.64s' ‚Í key '%-.64s' ‚É‚¨‚¢‚Äd•¡‚µ‚Ä‚¢‚Ü‚·", - est "Kattuv väärtus '%-.64s' võtmele '%-.64s'" - fre "Duplicata du champ '%-.64s' pour la clef '%-.64s'" - ger "Doppelter Eintrag '%-.64s' für Schlüssel '%-.64s'" - greek "ÄéðëÞ åããñáöÞ '%-.64s' ãéá ôï êëåéäß '%-.64s'" - hun "Duplikalt bejegyzes '%-.64s' a '%-.64s' kulcs szerint." - ita "Valore duplicato '%-.64s' per la chiave '%-.64s'" - jpn "'%-.64s' ¤Ï key '%-.64s' ¤Ë¤ª¤¤¤Æ½ÅÊ£¤·¤Æ¤¤¤Þ¤¹" - kor "Áߺ¹µÈ ÀÔ·Â °ª '%-.64s': key '%-.64s'" - nor "Like verdier '%-.64s' for nøkkel '%-.64s'" - norwegian-ny "Like verdiar '%-.64s' for nykkel '%-.64s'" - pol "Powtórzone wyst?pienie '%-.64s' dla klucza '%-.64s'" - por "Entrada '%-.64s' duplicada para a chave '%-.64s'" - rum "Cimpul '%-.64s' e duplicat pentru cheia '%-.64s'" - rus "äÕÂÌÉÒÕÀÝÁÑÓÑ ÚÁÐÉÓØ '%-.64s' ÐÏ ËÌÀÞÕ '%-.64s'" - serbian "Dupliran unos '%-.64s' za kljuè '%-.64s'" - slo "Opakovaný kµúè '%-.64s' (èíslo kµúèa '%-.64s')" - spa "Entrada duplicada '%-.64s' para la clave '%-.64s'" - swe "Dubbel nyckel '%-.64s' för nyckel '%-.64s'" - ukr "äÕÂÌÀÀÞÉÊ ÚÁÐÉÓ '%-.64s' ÄÌÑ ËÌÀÞÁ '%-.64s'" + cze "Zdvojen-Bý klíè '%-.64s' (èíslo klíèe %d)" + dan "Ens værdier '%-.64s' for indeks %d" + nla "Dubbele ingang '%-.64s' voor zoeksleutel %d" + eng "Duplicate entry '%-.64s' for key %d" + jps "'%-.64s' ‚Í key %d ‚É‚¨‚¢‚Äd•¡‚µ‚Ä‚¢‚Ü‚·", + est "Kattuv väärtus '%-.64s' võtmele %d" + fre "Duplicata du champ '%-.64s' pour la clef %d" + ger "Doppelter Eintrag '%-.64s' für Schlüssel %d" + greek "ÄéðëÞ åããñáöÞ '%-.64s' ãéá ôï êëåéäß %d" + hun "Duplikalt bejegyzes '%-.64s' a %d kulcs szerint." + ita "Valore duplicato '%-.64s' per la chiave %d" + jpn "'%-.64s' ¤Ï key %d ¤Ë¤ª¤¤¤Æ½ÅÊ£¤·¤Æ¤¤¤Þ¤¹" + kor "Áߺ¹µÈ ÀÔ·Â °ª '%-.64s': key %d" + nor "Like verdier '%-.64s' for nøkkel %d" + norwegian-ny "Like verdiar '%-.64s' for nykkel %d" + pol "Powtórzone wyst?pienie '%-.64s' dla klucza %d" + por "Entrada '%-.64s' duplicada para a chave %d" + rum "Cimpul '%-.64s' e duplicat pentru cheia %d" + rus "äÕÂÌÉÒÕÀÝÁÑÓÑ ÚÁÐÉÓØ '%-.64s' ÐÏ ËÌÀÞÕ %d" + serbian "Dupliran unos '%-.64s' za kljuè '%d'" + slo "Opakovaný kµúè '%-.64s' (èíslo kµúèa %d)" + spa "Entrada duplicada '%-.64s' para la clave %d" + swe "Dubbel nyckel '%-.64s' för nyckel %d" + ukr "äÕÂÌÀÀÞÉÊ ÚÁÐÉÓ '%-.64s' ÄÌÑ ËÌÀÞÁ %d" ER_WRONG_FIELD_SPEC 42000 S1009 cze "Chybn-Bá specifikace sloupce '%-.64s'" dan "Forkert kolonnespecifikaton for felt '%-.64s'" @@ -1526,30 +1526,30 @@ ER_WRONG_FIELD_SPEC 42000 S1009 spa "Especificador de columna erroneo para la columna '%-.64s'" swe "Felaktigt kolumntyp för kolumn '%-.64s'" ukr "îÅצÒÎÉÊ ÓÐÅÃÉƦËÁÔÏÒ ÓÔÏ×ÂÃÑ '%-.64s'" -ER_PARSE_ERROR 42000 - cze "%s bl-Bízko '%-.64s' na øádku %d" - dan "%s nær '%-.64s' på linje %d" - nla "%s bij '%-.64s' in regel %d" +ER_PARSE_ERROR 42000 s1009 + cze "%s bl-Bízko '%-.80s' na øádku %d" + dan "%s nær '%-.80s' på linje %d" + nla "%s bij '%-.80s' in regel %d" eng "%s near '%-.80s' at line %d" jps "%s : '%-.80s' •t‹ß : %d s–Ú", est "%s '%-.80s' ligidal real %d" - fre "%s près de '%-.64s' à la ligne %d" + fre "%s près de '%-.80s' à la ligne %d" ger "%s bei '%-.80s' in Zeile %d" greek "%s ðëçóßïí '%-.80s' óôç ãñáììÞ %d" hun "A %s a '%-.80s'-hez kozeli a %d sorban" ita "%s vicino a '%-.80s' linea %d" jpn "%s : '%-.80s' ÉÕ¶á : %d ¹ÔÌÜ" - kor "'%-.64s' ¿¡·¯ °°À¾´Ï´Ù. ('%-.80s' ¸í·É¾î ¶óÀÎ %d)" - nor "%s nær '%-.64s' på linje %d" - norwegian-ny "%s attmed '%-.64s' på line %d" - pol "%s obok '%-.64s' w linii %d" + kor "'%s' ¿¡·¯ °°À¾´Ï´Ù. ('%-.80s' ¸í·É¾î ¶óÀÎ %d)" + nor "%s nær '%-.80s' på linje %d" + norwegian-ny "%s attmed '%-.80s' på line %d" + pol "%s obok '%-.80s' w linii %d" por "%s próximo a '%-.80s' na linha %d" rum "%s linga '%-.80s' pe linia %d" rus "%s ÏËÏÌÏ '%-.80s' ÎÁ ÓÔÒÏËÅ %d" serbian "'%s' u iskazu '%-.80s' na liniji %d" slo "%s blízko '%-.80s' na riadku %d" - spa "%s cerca '%-.64s' en la linea %d" - swe "%s nära '%-.64s' på rad %d" + spa "%s cerca '%-.80s' en la linea %d" + swe "%s nära '%-.80s' på rad %d" ukr "%s ¦ÌÑ '%-.80s' × ÓÔÒÏæ %d" ER_EMPTY_QUERY 42000 cze "V-Býsledek dotazu je prázdný" @@ -1821,30 +1821,30 @@ ER_WRONG_AUTO_KEY 42000 S1009 swe "Det får finnas endast ett AUTO_INCREMENT-fält och detta måste vara en nyckel" ukr "îÅצÒÎÅ ×ÉÚÎÁÞÅÎÎÑ ÔÁÂÌÉæ; íÏÖÅ ÂÕÔÉ ÌÉÛÅ ÏÄÉÎ Á×ÔÏÍÁÔÉÞÎÉÊ ÓÔÏ×ÂÅÃØ, ÝÏ ÐÏ×ÉÎÅÎ ÂÕÔÉ ×ÉÚÎÁÞÅÎÉÊ ÑË ËÌÀÞ" ER_READY - cze "%s: p-Bøipraven na spojení" - dan "%s: klar til tilslutninger" - nla "%s: klaar voor verbindingen" + cze "%s: p-Bøipraven na spojení\nVersion: '%s' socket: '%s' port: %d"" + dan "%s: klar til tilslutninger\nVersion: '%s' socket: '%s' port: %d"" + nla "%s: klaar voor verbindingen\nVersion: '%s' socket: '%s' port: %d"" eng "%s: ready for connections.\nVersion: '%s' socket: '%s' port: %d" - jps "%s: €”õŠ®—¹", - est "%s: ootab ühendusi" - fre "%s: Prêt pour des connections" - ger "%-.64s: Bereit für Verbindungen.\nVersion: '%2' Socket: '%s' Port: %d" - greek "%s: óå áíáìïíÞ óõíäÝóåùí" - hun "%s: kapcsolatra kesz" - ita "%s: Pronto per le connessioni\n" - jpn "%s: ½àÈ÷´°Î»" - kor "%s: ¿¬°á ÁغñÁßÀÔ´Ï´Ù" - nor "%s: klar for tilkoblinger" - norwegian-ny "%s: klar for tilkoblingar" - pol "%s: gotowe do po³?czenia" - por "%s: Pronto para conexões" - rum "%s: sint gata pentru conectii" - rus "%s: çÏÔÏ× ÐÒÉÎÉÍÁÔØ ÓÏÅÄÉÎÅÎÉÑ.\n÷ÅÒÓÉÑ: '%s' ÓÏËÅÔ: '%s' ÐÏÒÔ: %d %s" - serbian "%s: Spreman za konekcije\n" - slo "%s: pripravený na spojenie" - spa "%s: preparado para conexiones" - swe "%s: klar att ta emot klienter" - ukr "%s: çÏÔÏ×ÉÊ ÄÌÑ Ú'¤ÄÎÁÎØ!" + jps "%s: €”õŠ®—¹\nVersion: '%s' socket: '%s' port: %d"", + est "%s: ootab ühendusi\nVersion: '%s' socket: '%s' port: %d"" + fre "%s: Prêt pour des connexions\nVersion: '%s' socket: '%s' port: %d"" + ger "%s: Bereit für Verbindungen.\nVersion: '%s' Socket: '%s' Port: %d" + greek "%s: óå áíáìïíÞ óõíäÝóåùí\nVersion: '%s' socket: '%s' port: %d"" + hun "%s: kapcsolatra kesz\nVersion: '%s' socket: '%s' port: %d"" + ita "%s: Pronto per le connessioni\nVersion: '%s' socket: '%s' port: %d"" + jpn "%s: ½àÈ÷´°Î»\nVersion: '%s' socket: '%s' port: %d"" + kor "%s: ¿¬°á ÁغñÁßÀÔ´Ï´Ù\nVersion: '%s' socket: '%s' port: %d"" + nor "%s: klar for tilkoblinger\nVersion: '%s' socket: '%s' port: %d"" + norwegian-ny "%s: klar for tilkoblingar\nVersion: '%s' socket: '%s' port: %d"" + pol "%s: gotowe do po³?czenia\nVersion: '%s' socket: '%s' port: %d"" + por "%s: Pronto para conexões\nVersion: '%s' socket: '%s' port: %d"" + rum "%s: sint gata pentru conectii\nVersion: '%s' socket: '%s' port: %d"" + rus "%s: çÏÔÏ× ÐÒÉÎÉÍÁÔØ ÓÏÅÄÉÎÅÎÉÑ.\n÷ÅÒÓÉÑ: '%s' ÓÏËÅÔ: '%s' ÐÏÒÔ: %d" + serbian "%s: Spreman za konekcije\nVersion: '%s' socket: '%s' port: %d"" + slo "%s: pripravený na spojenie\nVersion: '%s' socket: '%s' port: %d"" + spa "%s: preparado para conexiones\nVersion: '%s' socket: '%s' port: %d"" + swe "%s: klar att ta emot klienter\nVersion: '%s' socket: '%s' port: %d"" + ukr "%s: çÏÔÏ×ÉÊ ÄÌÑ Ú'¤ÄÎÁÎØ!\nVersion: '%s' socket: '%s' port: %d"" ER_NORMAL_SHUTDOWN cze "%s: norm-Bální ukonèení\n" dan "%s: Normal nedlukning\n" @@ -1852,7 +1852,7 @@ ER_NORMAL_SHUTDOWN eng "%s: Normal shutdown\n" est "%s: MySQL lõpetas\n" fre "%s: Arrêt normal du serveur\n" - ger "%-.64s: Normal heruntergefahren\n" + ger "%s: Normal heruntergefahren\n" greek "%s: ÖõóéïëïãéêÞ äéáäéêáóßá shutdown\n" hun "%s: Normal leallitas\n" ita "%s: Shutdown normale\n" @@ -1876,7 +1876,7 @@ ER_GOT_SIGNAL jps "%s: Got signal %d. ’†’f!\n", est "%s: sain signaali %d. Lõpetan!\n" fre "%s: Reçu le signal %d. Abandonne!\n" - ger "%-.64s: Signal %d erhalten. Abbruch!\n" + ger "%s: Signal %d erhalten. Abbruch!\n" greek "%s: ÅëÞöèç ôï ìÞíõìá %d. Ç äéáäéêáóßá åãêáôáëåßðåôáé!\n" hun "%s: %d jelzes. Megszakitva!\n" ita "%s: Ricevuto segnale %d. Interruzione!\n" @@ -1901,7 +1901,7 @@ ER_SHUTDOWN_COMPLETE jps "%s: Shutdown Š®—¹\n", est "%s: Lõpp\n" fre "%s: Arrêt du serveur terminé\n" - ger "%-.64s: Herunterfahren beendet\n" + ger "%s: Herunterfahren beendet\n" greek "%s: Ç äéáäéêáóßá Shutdown ïëïêëçñþèçêå\n" hun "%s: A leallitas kesz\n" ita "%s: Shutdown completato\n" @@ -1919,29 +1919,29 @@ ER_SHUTDOWN_COMPLETE swe "%s: Avslutning klar\n" ukr "%s: òÏÂÏÔÕ ÚÁ×ÅÒÛÅÎÏ\n" ER_FORCING_CLOSE 08S01 - cze "%s: n-Básilné uzavøení threadu %ld u¾ivatele '%-.64s'\n" - dan "%s: Forceret nedlukning af tråd: %ld bruger: '%-.64s'\n" - nla "%s: Afsluiten afgedwongen van thread %ld gebruiker: '%-.64s'\n" + cze "%s: n-Básilné uzavøení threadu %ld u¾ivatele '%-.32s'\n" + dan "%s: Forceret nedlukning af tråd: %ld bruger: '%-.32s'\n" + nla "%s: Afsluiten afgedwongen van thread %ld gebruiker: '%-.32s'\n" eng "%s: Forcing close of thread %ld user: '%-.32s'\n" - jps "%s: ƒXƒŒƒbƒh %ld ‹§I—¹ user: '%-.64s'\n", + jps "%s: ƒXƒŒƒbƒh %ld ‹§I—¹ user: '%-.32s'\n", est "%s: Sulgen jõuga lõime %ld kasutaja: '%-.32s'\n" - fre "%s: Arrêt forcé de la tâche (thread) %ld utilisateur: '%-.64s'\n" + fre "%s: Arrêt forcé de la tâche (thread) %ld utilisateur: '%-.32s'\n" ger "%s: Thread %ld zwangsweise beendet. Benutzer: '%-.32s'\n" - greek "%s: Ôï thread èá êëåßóåé %ld user: '%-.64s'\n" - hun "%s: A(z) %ld thread kenyszeritett zarasa. Felhasznalo: '%-.64s'\n" - ita "%s: Forzata la chiusura del thread %ld utente: '%-.64s'\n" - jpn "%s: ¥¹¥ì¥Ã¥É %ld ¶¯À©½ªÎ» user: '%-.64s'\n" - kor "%s: thread %ldÀÇ °Á¦ Á¾·á user: '%-.64s'\n" - nor "%s: Påtvinget avslutning av tråd %ld bruker: '%-.64s'\n" - norwegian-ny "%s: Påtvinga avslutning av tråd %ld brukar: '%-.64s'\n" - pol "%s: Wymuszenie zamkniêcia w?tku %ld u¿ytkownik: '%-.64s'\n" + greek "%s: Ôï thread èá êëåßóåé %ld user: '%-.32s'\n" + hun "%s: A(z) %ld thread kenyszeritett zarasa. Felhasznalo: '%-.32s'\n" + ita "%s: Forzata la chiusura del thread %ld utente: '%-.32s'\n" + jpn "%s: ¥¹¥ì¥Ã¥É %ld ¶¯À©½ªÎ» user: '%-.32s'\n" + kor "%s: thread %ldÀÇ °Á¦ Á¾·á user: '%-.32s'\n" + nor "%s: Påtvinget avslutning av tråd %ld bruker: '%-.32s'\n" + norwegian-ny "%s: Påtvinga avslutning av tråd %ld brukar: '%-.32s'\n" + pol "%s: Wymuszenie zamkniêcia w?tku %ld u¿ytkownik: '%-.32s'\n" por "%s: Forçando finalização da 'thread' %ld - usuário '%-.32s'\n" rum "%s: Terminare fortata a thread-ului %ld utilizatorului: '%-.32s'\n" rus "%s: ðÒÉÎÕÄÉÔÅÌØÎÏ ÚÁËÒÙ×ÁÅÍ ÐÏÔÏË %ld ÐÏÌØÚÏ×ÁÔÅÌÑ: '%-.32s'\n" serbian "%s: Usiljeno gašenje thread-a %ld koji pripada korisniku: '%-.32s'\n" - slo "%s: násilné ukonèenie vlákna %ld u¾ívateµa '%-.64s'\n" - spa "%s: Forzando a cerrar el thread %ld usuario: '%-.64s'\n" - swe "%s: Stänger av tråd %ld; användare: '%-.64s'\n" + slo "%s: násilné ukonèenie vlákna %ld u¾ívateµa '%-.32s'\n" + spa "%s: Forzando a cerrar el thread %ld usuario: '%-.32s'\n" + swe "%s: Stänger av tråd %ld; användare: '%-.32s'\n" ukr "%s: ðÒÉÓËÏÒÀÀ ÚÁËÒÉÔÔÑ Ç¦ÌËÉ %ld ËÏÒÉÓÔÕ×ÁÞÁ: '%-.32s'\n" ER_IPSOCK_ERROR 08S01 cze "Nemohu vytvo-Bøit IP socket" @@ -1950,7 +1950,7 @@ ER_IPSOCK_ERROR 08S01 eng "Can't create IP socket" jps "IP socket ‚ªì‚ê‚Ü‚¹‚ñ", est "Ei suuda luua IP socketit" - fre "Ne peut créer la connection IP (socket)" + fre "Ne peut créer la connexion IP (socket)" ger "Kann IP-Socket nicht erzeugen" greek "Äåí åßíáé äõíáôÞ ç äçìéïõñãßá IP socket" hun "Az IP socket nem hozhato letre" @@ -2041,55 +2041,55 @@ ER_BLOBS_AND_NO_TERMINATED 42000 S1009 swe "Man kan inte använda fast radlängd med blobs. Använd 'fields terminated by'" ukr "îÅ ÍÏÖÎÁ ×ÉËÏÒÉÓÔÏ×Õ×ÁÔÉ ÓÔÁÌÕ ÄÏ×ÖÉÎÕ ÓÔÒÏËÉ Ú BLOB. úËÏÒÉÓÔÁÊÔÅÓÑ 'fields terminated by'" ER_TEXTFILE_NOT_READABLE - cze "Soubor '%-.64s' mus-Bí být v adresáøi databáze nebo èitelný pro v¹echny" - dan "Filen '%-.64s' skal være i database-folderen og kunne læses af alle" - nla "Het bestand '%-.64s' dient in de database directory voor the komen of leesbaar voor iedereen te zijn." + cze "Soubor '%-.128s' mus-Bí být v adresáøi databáze nebo èitelný pro v¹echny" + dan "Filen '%-.128s' skal være i database-folderen og kunne læses af alle" + nla "Het bestand '%-.128s' dient in de database directory voor the komen of leesbaar voor iedereen te zijn." eng "The file '%-.128s' must be in the database directory or be readable by all" - jps "ƒtƒ@ƒCƒ‹ '%-.64s' ‚Í databse ‚Ì directory ‚É‚ ‚é‚©‘S‚Ẵ†[ƒU[‚ª“Ç‚ß‚é‚悤‚É‹–‰Â‚³‚ê‚Ä‚¢‚È‚¯‚ê‚΂Ȃè‚Ü‚¹‚ñ.", - est "Fail '%-.64s' peab asuma andmebaasi kataloogis või olema kõigile loetav" - fre "Le fichier '%-.64s' doit être dans le répertoire de la base et lisible par tous" - ger "Datei '%-.64s' muss im Datenbank-Verzeichnis vorhanden oder lesbar für alle sein" - greek "Ôï áñ÷åßï '%-.64s' ðñÝðåé íá õðÜñ÷åé óôï database directory Þ íá ìðïñåß íá äéáâáóôåß áðü üëïõò" - hun "A(z) '%-.64s'-nak az adatbazis konyvtarban kell lennie, vagy mindenki szamara olvashatonak" - ita "Il file '%-.64s' deve essere nella directory del database e deve essere leggibile da tutti" - jpn "¥Õ¥¡¥¤¥ë '%-.64s' ¤Ï databse ¤Î directory ¤Ë¤¢¤ë¤«Á´¤Æ¤Î¥æ¡¼¥¶¡¼¤¬Æɤá¤ë¤è¤¦¤Ëµö²Ä¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó." - kor "'%-.64s' ÈÀÏ´Â µ¥ÀÌŸº£À̽º µð·ºÅ丮¿¡ Á¸ÀçÇϰųª ¸ðµÎ¿¡°Ô Àб⠰¡´ÉÇÏ¿©¾ß ÇÕ´Ï´Ù." - nor "Filen '%-.64s' må være i database-katalogen for å være lesbar for alle" - norwegian-ny "Filen '%-.64s' må være i database-katalogen for å være lesbar for alle" - pol "Plik '%-.64s' musi znajdowaæ sie w katalogu bazy danych lub mieæ prawa czytania przez wszystkich" - por "Arquivo '%-.64s' tem que estar no diretório do banco de dados ou ter leitura possível para todos" - rum "Fisierul '%-.64s' trebuie sa fie in directorul bazei de data sau trebuie sa poata sa fie citit de catre toata lumea (verifica permisiile)" - rus "æÁÊÌ '%-.64s' ÄÏÌÖÅÎ ÎÁÈÏÄÉÔØÓÑ × ÔÏÍ ÖÅ ËÁÔÁÌÏÇÅ, ÞÔÏ É ÂÁÚÁ ÄÁÎÎÙÈ, ÉÌÉ ÂÙÔØ ÏÂÝÅÄÏÓÔÕÐÎÙÍ ÄÌÑ ÞÔÅÎÉÑ" - serbian "File '%-.64s' mora biti u direktorijumu gde su file-ovi baze i mora imati odgovarajuæa prava pristupa" - slo "Súbor '%-.64s' musí by» v adresári databázy, alebo èitateµný pre v¹etkých" - spa "El archivo '%-.64s' debe estar en el directorio de la base de datos o ser de lectura por todos" - swe "Textfilen '%.64s' måste finnas i databasbiblioteket eller vara läsbar för alla" - ukr "æÁÊÌ '%-.64s' ÐÏ×ÉÎÅÎ ÂÕÔÉ Õ ÔÅæ ÂÁÚÉ ÄÁÎÎÉÈ ÁÂÏ ÍÁÔÉ ×ÓÔÁÎÏ×ÌÅÎÅ ÐÒÁ×Ï ÎÁ ÞÉÔÁÎÎÑ ÄÌÑ ÕÓ¦È" + jps "ƒtƒ@ƒCƒ‹ '%-.128s' ‚Í databse ‚Ì directory ‚É‚ ‚é‚©‘S‚Ẵ†[ƒU[‚ª“Ç‚ß‚é‚悤‚É‹–‰Â‚³‚ê‚Ä‚¢‚È‚¯‚ê‚΂Ȃè‚Ü‚¹‚ñ.", + est "Fail '%-.128s' peab asuma andmebaasi kataloogis või olema kõigile loetav" + fre "Le fichier '%-.128s' doit être dans le répertoire de la base et lisible par tous" + ger "Datei '%-.128s' muss im Datenbank-Verzeichnis vorhanden oder lesbar für alle sein" + greek "Ôï áñ÷åßï '%-.128s' ðñÝðåé íá õðÜñ÷åé óôï database directory Þ íá ìðïñåß íá äéáâáóôåß áðü üëïõò" + hun "A(z) '%-.128s'-nak az adatbazis konyvtarban kell lennie, vagy mindenki szamara olvashatonak" + ita "Il file '%-.128s' deve essere nella directory del database e deve essere leggibile da tutti" + jpn "¥Õ¥¡¥¤¥ë '%-.128s' ¤Ï databse ¤Î directory ¤Ë¤¢¤ë¤«Á´¤Æ¤Î¥æ¡¼¥¶¡¼¤¬Æɤá¤ë¤è¤¦¤Ëµö²Ä¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó." + kor "'%-.128s' ÈÀÏ´Â µ¥ÀÌŸº£À̽º µð·ºÅ丮¿¡ Á¸ÀçÇϰųª ¸ðµÎ¿¡°Ô Àб⠰¡´ÉÇÏ¿©¾ß ÇÕ´Ï´Ù." + nor "Filen '%-.128s' må være i database-katalogen for å være lesbar for alle" + norwegian-ny "Filen '%-.128s' må være i database-katalogen for å være lesbar for alle" + pol "Plik '%-.128s' musi znajdowaæ sie w katalogu bazy danych lub mieæ prawa czytania przez wszystkich" + por "Arquivo '%-.128s' tem que estar no diretório do banco de dados ou ter leitura possível para todos" + rum "Fisierul '%-.128s' trebuie sa fie in directorul bazei de data sau trebuie sa poata sa fie citit de catre toata lumea (verifica permisiile)" + rus "æÁÊÌ '%-.128s' ÄÏÌÖÅÎ ÎÁÈÏÄÉÔØÓÑ × ÔÏÍ ÖÅ ËÁÔÁÌÏÇÅ, ÞÔÏ É ÂÁÚÁ ÄÁÎÎÙÈ, ÉÌÉ ÂÙÔØ ÏÂÝÅÄÏÓÔÕÐÎÙÍ ÄÌÑ ÞÔÅÎÉÑ" + serbian "File '%-.128s' mora biti u direktorijumu gde su file-ovi baze i mora imati odgovarajuæa prava pristupa" + slo "Súbor '%-.128s' musí by» v adresári databázy, alebo èitateµný pre v¹etkých" + spa "El archivo '%-.128s' debe estar en el directorio de la base de datos o ser de lectura por todos" + swe "Textfilen '%-.128s' måste finnas i databasbiblioteket eller vara läsbar för alla" + ukr "æÁÊÌ '%-.128s' ÐÏ×ÉÎÅÎ ÂÕÔÉ Õ ÔÅæ ÂÁÚÉ ÄÁÎÎÉÈ ÁÂÏ ÍÁÔÉ ×ÓÔÁÎÏ×ÌÅÎÅ ÐÒÁ×Ï ÎÁ ÞÉÔÁÎÎÑ ÄÌÑ ÕÓ¦È" ER_FILE_EXISTS_ERROR - cze "Soubor '%-.64s' ji-B¾ existuje" - dan "Filen '%-.64s' eksisterer allerede" - nla "Het bestand '%-.64s' bestaat reeds" + cze "Soubor '%-.200s' ji-B¾ existuje" + dan "Filen '%-.200s' eksisterer allerede" + nla "Het bestand '%-.200s' bestaat reeds" eng "File '%-.200s' already exists" - jps "File '%-.64s' ‚ÍŠù‚É‘¶Ý‚µ‚Ü‚·", - est "Fail '%-.80s' juba eksisteerib" - fre "Le fichier '%-.64s' existe déjà" - ger "Datei '%-.80s' bereits vorhanden" - greek "Ôï áñ÷åßï '%-.64s' õðÜñ÷åé Þäç" - hun "A '%-.64s' file mar letezik." - ita "Il file '%-.64s' esiste gia`" - jpn "File '%-.64s' ¤Ï´û¤Ë¸ºß¤·¤Þ¤¹" - kor "'%-.64s' ÈÀÏÀº ÀÌ¹Ì Á¸ÀçÇÕ´Ï´Ù." - nor "Filen '%-.64s' eksisterte allerede" - norwegian-ny "Filen '%-.64s' eksisterte allereide" - pol "Plik '%-.64s' ju¿ istnieje" - por "Arquivo '%-.80s' já existe" - rum "Fisierul '%-.80s' exista deja" - rus "æÁÊÌ '%-.80s' ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ" - serbian "File '%-.80s' veæ postoji" - slo "Súbor '%-.64s' u¾ existuje" - spa "El archivo '%-.64s' ya existe" - swe "Filen '%-.64s' existerar redan" - ukr "æÁÊÌ '%-.80s' ×ÖÅ ¦ÓÎÕ¤" + jps "File '%-.200s' ‚ÍŠù‚É‘¶Ý‚µ‚Ü‚·", + est "Fail '%-.200s' juba eksisteerib" + fre "Le fichier '%-.200s' existe déjà" + ger "Datei '%-.200s' bereits vorhanden" + greek "Ôï áñ÷åßï '%-.200s' õðÜñ÷åé Þäç" + hun "A '%-.200s' file mar letezik." + ita "Il file '%-.200s' esiste gia`" + jpn "File '%-.200s' ¤Ï´û¤Ë¸ºß¤·¤Þ¤¹" + kor "'%-.200s' ÈÀÏÀº ÀÌ¹Ì Á¸ÀçÇÕ´Ï´Ù." + nor "Filen '%-.200s' eksisterte allerede" + norwegian-ny "Filen '%-.200s' eksisterte allereide" + pol "Plik '%-.200s' ju¿ istnieje" + por "Arquivo '%-.200s' já existe" + rum "Fisierul '%-.200s' exista deja" + rus "æÁÊÌ '%-.200s' ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ" + serbian "File '%-.200s' veæ postoji" + slo "Súbor '%-.200s' u¾ existuje" + spa "El archivo '%-.200s' ya existe" + swe "Filen '%-.200s' existerar redan" + ukr "æÁÊÌ '%-.200s' ×ÖÅ ¦ÓÎÕ¤" ER_LOAD_INFO cze "Z-Báznamù: %ld Vymazáno: %ld Pøeskoèeno: %ld Varování: %ld" dan "Poster: %ld Fjernet: %ld Sprunget over: %ld Advarsler: %ld" @@ -2319,51 +2319,51 @@ ER_NO_TABLES_USED swe "Inga tabeller angivna" ukr "îÅ ×ÉËÏÒÉÓÔÁÎÏ ÔÁÂÌÉÃØ" ER_TOO_BIG_SET - cze "P-Bøíli¹ mnoho øetìzcù pro sloupec %s a SET" + cze "P-Bøíli¹ mnoho øetìzcù pro sloupec %-.64s a SET" dan "For mange tekststrenge til specifikationen af SET i kolonne %-.64s" - nla "Teveel strings voor kolom %s en SET" + nla "Teveel strings voor kolom %-.64s en SET" eng "Too many strings for column %-.64s and SET" est "Liiga palju string tulbale %-.64s tüübile SET" - fre "Trop de chaînes dans la colonne %s avec SET" + fre "Trop de chaînes dans la colonne %-.64s avec SET" ger "Zu viele Strings für Feld %-.64s und SET angegeben" greek "ÐÜñá ðïëëÜ strings ãéá ôï ðåäßï %-.64s êáé SET" hun "Tul sok karakter: %-.64s es SET" ita "Troppe stringhe per la colonna %-.64s e la SET" kor "Ä®·³ %-.64s¿Í SET¿¡¼ ½ºÆ®¸µÀÌ ³Ê¹« ¸¹½À´Ï´Ù." - nor "For mange tekststrenger kolonne %s og SET" - norwegian-ny "For mange tekststrengar felt %s og SET" - pol "Zbyt wiele ³añcuchów dla kolumny %s i polecenia SET" + nor "For mange tekststrenger kolonne %-.64s og SET" + norwegian-ny "For mange tekststrengar felt %-.64s og SET" + pol "Zbyt wiele ³añcuchów dla kolumny %-.64s i polecenia SET" por "'Strings' demais para coluna '%-.64s' e SET" rum "Prea multe siruri pentru coloana %-.64s si SET" rus "óÌÉÛËÏÍ ÍÎÏÇÏ ÚÎÁÞÅÎÉÊ ÄÌÑ ÓÔÏÌÂÃÁ %-.64s × SET" serbian "Previše string-ova za kolonu '%-.64s' i komandu 'SET'" slo "Príli¹ mnoho re»azcov pre pole %-.64s a SET" - spa "Muchas strings para columna %s y SET" - swe "För många alternativ till kolumn %s för SET" + spa "Muchas strings para columna %-.64s y SET" + swe "För många alternativ till kolumn %-.64s för SET" ukr "úÁÂÁÇÁÔÏ ÓÔÒÏË ÄÌÑ ÓÔÏ×ÂÃÑ %-.64s ÔÁ SET" ER_NO_UNIQUE_LOGFILE - cze "Nemohu vytvo-Bøit jednoznaèné jméno logovacího souboru %s.(1-999)\n" - dan "Kan ikke lave unikt log-filnavn %s.(1-999)\n" - nla "Het is niet mogelijk een unieke naam te maken voor de logfile %s.(1-999)\n" + cze "Nemohu vytvo-Bøit jednoznaèné jméno logovacího souboru %-.200s.(1-999)\n" + dan "Kan ikke lave unikt log-filnavn %-.200s.(1-999)\n" + nla "Het is niet mogelijk een unieke naam te maken voor de logfile %-.200s.(1-999)\n" eng "Can't generate a unique log-filename %-.200s.(1-999)\n" - est "Ei suuda luua unikaalset logifaili nime %-.64s.(1-999)\n" - fre "Ne peut générer un unique nom de journal %s.(1-999)\n" - ger "Kann keinen eindeutigen Dateinamen für die Logdatei %-.64s(1-999) erzeugen\n" - greek "Áäýíáôç ç äçìéïõñãßá unique log-filename %-.64s.(1-999)\n" - hun "Egyedi log-filenev nem generalhato: %-.64s.(1-999)\n" - ita "Impossibile generare un nome del file log unico %-.64s.(1-999)\n" - kor "Unique ·Î±×ÈÀÏ '%-.64s'¸¦ ¸¸µé¼ö ¾ø½À´Ï´Ù.(1-999)\n" - nor "Kan ikke lage unikt loggfilnavn %s.(1-999)\n" - norwegian-ny "Kan ikkje lage unikt loggfilnavn %s.(1-999)\n" - pol "Nie mo¿na stworzyæ unikalnej nazwy pliku z logiem %s.(1-999)\n" - por "Não pode gerar um nome de arquivo de 'log' único '%-.64s'.(1-999)\n" - rum "Nu pot sa generez un nume de log unic %-.64s.(1-999)\n" - rus "îÅ×ÏÚÍÏÖÎÏ ÓÏÚÄÁÔØ ÕÎÉËÁÌØÎÏÅ ÉÍÑ ÆÁÊÌÁ ÖÕÒÎÁÌÁ %-.64s.(1-999)\n" - serbian "Ne mogu da generišem jedinstveno ime log-file-a: '%-.64s.(1-999)'\n" - slo "Nemô¾em vytvori» unikátne meno log-súboru %-.64s.(1-999)\n" - spa "No puede crear un unico archivo log %s.(1-999)\n" - swe "Kan inte generera ett unikt filnamn %s.(1-999)\n" - ukr "îÅ ÍÏÖÕ ÚÇÅÎÅÒÕ×ÁÔÉ ÕΦËÁÌØÎÅ ¦Í'Ñ log-ÆÁÊÌÕ %-.64s.(1-999)\n" + est "Ei suuda luua unikaalset logifaili nime %-.200s.(1-999)\n" + fre "Ne peut générer un unique nom de journal %-.200s.(1-999)\n" + ger "Kann keinen eindeutigen Dateinamen für die Logdatei %-.200s(1-999) erzeugen\n" + greek "Áäýíáôç ç äçìéïõñãßá unique log-filename %-.200s.(1-999)\n" + hun "Egyedi log-filenev nem generalhato: %-.200s.(1-999)\n" + ita "Impossibile generare un nome del file log unico %-.200s.(1-999)\n" + kor "Unique ·Î±×ÈÀÏ '%-.200s'¸¦ ¸¸µé¼ö ¾ø½À´Ï´Ù.(1-999)\n" + nor "Kan ikke lage unikt loggfilnavn %-.200s.(1-999)\n" + norwegian-ny "Kan ikkje lage unikt loggfilnavn %-.200s.(1-999)\n" + pol "Nie mo¿na stworzyæ unikalnej nazwy pliku z logiem %-.200s.(1-999)\n" + por "Não pode gerar um nome de arquivo de 'log' único '%-.200s'.(1-999)\n" + rum "Nu pot sa generez un nume de log unic %-.200s.(1-999)\n" + rus "îÅ×ÏÚÍÏÖÎÏ ÓÏÚÄÁÔØ ÕÎÉËÁÌØÎÏÅ ÉÍÑ ÆÁÊÌÁ ÖÕÒÎÁÌÁ %-.200s.(1-999)\n" + serbian "Ne mogu da generišem jedinstveno ime log-file-a: '%-.200s.(1-999)'\n" + slo "Nemô¾em vytvori» unikátne meno log-súboru %-.200s.(1-999)\n" + spa "No puede crear un unico archivo log %-.200s.(1-999)\n" + swe "Kan inte generera ett unikt filnamn %-.200s.(1-999)\n" + ukr "îÅ ÍÏÖÕ ÚÇÅÎÅÒÕ×ÁÔÉ ÕΦËÁÌØÎÅ ¦Í'Ñ log-ÆÁÊÌÕ %-.200s.(1-999)\n" ER_TABLE_NOT_LOCKED_FOR_WRITE cze "Tabulka '%-.64s' byla zam-Bèena s READ a nemù¾e být zmìnìna" dan "Tabellen '%-.64s' var låst med READ lås og kan ikke opdateres" @@ -2439,54 +2439,54 @@ ER_BLOB_CANT_HAVE_DEFAULT 42000 swe "BLOB fält '%-.64s' kan inte ha ett DEFAULT-värde" ukr "óÔÏ×ÂÅÃØ BLOB '%-.64s' ÎÅ ÍÏÖÅ ÍÁÔÉ ÚÎÁÞÅÎÎÑ ÐÏ ÚÁÍÏ×ÞÕ×ÁÎÎÀ" ER_WRONG_DB_NAME 42000 - cze "Nep-Bøípustné jméno databáze '%-.64s'" - dan "Ugyldigt database navn '%-.64s'" - nla "Databasenaam '%-.64s' is niet getoegestaan" + cze "Nep-Bøípustné jméno databáze '%-.100s'" + dan "Ugyldigt database navn '%-.100s'" + nla "Databasenaam '%-.100s' is niet getoegestaan" eng "Incorrect database name '%-.100s'" jps "Žw’肵‚½ database –¼ '%-.100s' ‚ªŠÔˆá‚Á‚Ä‚¢‚Ü‚·", est "Vigane andmebaasi nimi '%-.100s'" - fre "Nom de base de donnée illégal: '%-.64s'" + fre "Nom de base de donnée illégal: '%-.100s'" ger "Unerlaubter Datenbankname '%-.100s'" greek "ËÜèïò üíïìá âÜóçò äåäïìÝíùí '%-.100s'" hun "Hibas adatbazisnev: '%-.100s'" ita "Nome database errato '%-.100s'" jpn "»ØÄꤷ¤¿ database ̾ '%-.100s' ¤¬´Ö°ã¤Ã¤Æ¤¤¤Þ¤¹" kor "'%-.100s' µ¥ÀÌŸº£À̽ºÀÇ À̸§ÀÌ ºÎÁ¤È®ÇÕ´Ï´Ù." - nor "Ugyldig database navn '%-.64s'" - norwegian-ny "Ugyldig database namn '%-.64s'" - pol "Niedozwolona nazwa bazy danych '%-.64s'" + nor "Ugyldig database navn '%-.100s'" + norwegian-ny "Ugyldig database namn '%-.100s'" + pol "Niedozwolona nazwa bazy danych '%-.100s'" por "Nome de banco de dados '%-.100s' incorreto" rum "Numele bazei de date este incorect '%-.100s'" rus "îÅËÏÒÒÅËÔÎÏÅ ÉÍÑ ÂÁÚÙ ÄÁÎÎÙÈ '%-.100s'" serbian "Pogrešno ime baze '%-.100s'" slo "Neprípustné meno databázy '%-.100s'" - spa "Nombre de base de datos ilegal '%-.64s'" - swe "Felaktigt databasnamn '%-.64s'" + spa "Nombre de base de datos ilegal '%-.100s'" + swe "Felaktigt databasnamn '%-.100s'" ukr "îÅצÒÎÅ ¦Í'Ñ ÂÁÚÉ ÄÁÎÎÉÈ '%-.100s'" ER_WRONG_TABLE_NAME 42000 - cze "Nep-Bøípustné jméno tabulky '%-.64s'" - dan "Ugyldigt tabel navn '%-.64s'" - nla "Niet toegestane tabelnaam '%-.64s'" + cze "Nep-Bøípustné jméno tabulky '%-.100s'" + dan "Ugyldigt tabel navn '%-.100s'" + nla "Niet toegestane tabelnaam '%-.100s'" eng "Incorrect table name '%-.100s'" jps "Žw’肵‚½ table –¼ '%-.100s' ‚Í‚Ü‚¿‚ª‚Á‚Ä‚¢‚Ü‚·", est "Vigane tabeli nimi '%-.100s'" - fre "Nom de table illégal: '%-.64s'" + fre "Nom de table illégal: '%-.100s'" ger "Unerlaubter Tabellenname '%-.100s'" greek "ËÜèïò üíïìá ðßíáêá '%-.100s'" hun "Hibas tablanev: '%-.100s'" ita "Nome tabella errato '%-.100s'" jpn "»ØÄꤷ¤¿ table ̾ '%-.100s' ¤Ï¤Þ¤Á¤¬¤Ã¤Æ¤¤¤Þ¤¹" kor "'%-.100s' Å×À̺í À̸§ÀÌ ºÎÁ¤È®ÇÕ´Ï´Ù." - nor "Ugyldig tabell navn '%-.64s'" - norwegian-ny "Ugyldig tabell namn '%-.64s'" - pol "Niedozwolona nazwa tabeli '%-.64s'..." + nor "Ugyldig tabell navn '%-.100s'" + norwegian-ny "Ugyldig tabell namn '%-.100s'" + pol "Niedozwolona nazwa tabeli '%-.100s'..." por "Nome de tabela '%-.100s' incorreto" rum "Numele tabelei este incorect '%-.100s'" rus "îÅËÏÒÒÅËÔÎÏÅ ÉÍÑ ÔÁÂÌÉÃÙ '%-.100s'" serbian "Pogrešno ime tabele '%-.100s'" slo "Neprípustné meno tabuµky '%-.100s'" - spa "Nombre de tabla ilegal '%-.64s'" - swe "Felaktigt tabellnamn '%-.64s'" + spa "Nombre de tabla ilegal '%-.100s'" + swe "Felaktigt tabellnamn '%-.100s'" ukr "îÅצÒÎÅ ¦Í'Ñ ÔÁÂÌÉæ '%-.100s'" ER_TOO_BIG_SELECT 42000 cze "Zadan-Bý SELECT by procházel pøíli¹ mnoho záznamù a trval velmi dlouho. Zkontrolujte tvar WHERE a je-li SELECT v poøádku, pou¾ijte SET SQL_BIG_SELECTS=1" @@ -2534,97 +2534,97 @@ ER_UNKNOWN_ERROR swe "Oidentifierat fel" ukr "îÅצÄÏÍÁ ÐÏÍÉÌËÁ" ER_UNKNOWN_PROCEDURE 42000 - cze "Nezn-Bámá procedura %s" - dan "Ukendt procedure %s" - nla "Onbekende procedure %s" + cze "Nezn-Bámá procedura %-.64s" + dan "Ukendt procedure %-.64s" + nla "Onbekende procedure %-.64s" eng "Unknown procedure '%-.64s'" est "Tundmatu protseduur '%-.64s'" - fre "Procédure %s inconnue" + fre "Procédure %-.64s inconnue" ger "Unbekannte Prozedur '%-.64s'" greek "Áãíùóôç äéáäéêáóßá '%-.64s'" hun "Ismeretlen eljaras: '%-.64s'" ita "Procedura '%-.64s' sconosciuta" kor "¾Ë¼ö ¾ø´Â ¼öÇ๮ : '%-.64s'" - nor "Ukjent prosedyre %s" - norwegian-ny "Ukjend prosedyre %s" - pol "Unkown procedure %s" + nor "Ukjent prosedyre %-.64s" + norwegian-ny "Ukjend prosedyre %-.64s" + pol "Unkown procedure %-.64s" por "'Procedure' '%-.64s' desconhecida" rum "Procedura unknown '%-.64s'" rus "îÅÉÚ×ÅÓÔÎÁÑ ÐÒÏÃÅÄÕÒÁ '%-.64s'" serbian "Nepoznata procedura '%-.64s'" slo "Neznámá procedúra '%-.64s'" - spa "Procedimiento desconocido %s" - swe "Okänd procedur: %s" + spa "Procedimiento desconocido %-.64s" + swe "Okänd procedur: %-.64s" ukr "îÅצÄÏÍÁ ÐÒÏÃÅÄÕÒÁ '%-.64s'" ER_WRONG_PARAMCOUNT_TO_PROCEDURE 42000 - cze "Chybn-Bý poèet parametrù procedury %s" - dan "Forkert antal parametre til proceduren %s" - nla "Foutief aantal parameters doorgegeven aan procedure %s" + cze "Chybn-Bý poèet parametrù procedury %-.64s" + dan "Forkert antal parametre til proceduren %-.64s" + nla "Foutief aantal parameters doorgegeven aan procedure %-.64s" eng "Incorrect parameter count to procedure '%-.64s'" est "Vale parameetrite hulk protseduurile '%-.64s'" - fre "Mauvais nombre de paramètres pour la procedure %s" + fre "Mauvais nombre de paramètres pour la procedure %-.64s" ger "Falsche Parameterzahl für Prozedur '%-.64s'" greek "ËÜèïò áñéèìüò ðáñáìÝôñùí óôç äéáäéêáóßá '%-.64s'" hun "Rossz parameter a(z) '%-.64s'eljaras szamitasanal" ita "Numero di parametri errato per la procedura '%-.64s'" kor "'%-.64s' ¼öÇ๮¿¡ ´ëÇÑ ºÎÁ¤È®ÇÑ ÆĶó¸ÞÅÍ" - nor "Feil parameter antall til prosedyren %s" - norwegian-ny "Feil parameter tal til prosedyra %s" - pol "Incorrect parameter count to procedure %s" + nor "Feil parameter antall til prosedyren %-.64s" + norwegian-ny "Feil parameter tal til prosedyra %-.64s" + pol "Incorrect parameter count to procedure %-.64s" por "Número de parâmetros incorreto para a 'procedure' '%-.64s'" rum "Procedura '%-.64s' are un numar incorect de parametri" rus "îÅËÏÒÒÅËÔÎÏÅ ËÏÌÉÞÅÓÔ×Ï ÐÁÒÁÍÅÔÒÏ× ÄÌÑ ÐÒÏÃÅÄÕÒÙ '%-.64s'" serbian "Pogrešan broj parametara za proceduru '%-.64s'" slo "Chybný poèet parametrov procedúry '%-.64s'" - spa "Equivocado parametro count para procedimiento %s" - swe "Felaktigt antal parametrar till procedur %s" + spa "Equivocado parametro count para procedimiento %-.64s" + swe "Felaktigt antal parametrar till procedur %-.64s" ukr "èÉÂÎÁ ˦ÌØ˦ÓÔØ ÐÁÒÁÍÅÔÒ¦× ÐÒÏÃÅÄÕÒÉ '%-.64s'" ER_WRONG_PARAMETERS_TO_PROCEDURE - cze "Chybn-Bé parametry procedury %s" - dan "Forkert(e) parametre til proceduren %s" - nla "Foutieve parameters voor procedure %s" + cze "Chybn-Bé parametry procedury %-.64s" + dan "Forkert(e) parametre til proceduren %-.64s" + nla "Foutieve parameters voor procedure %-.64s" eng "Incorrect parameters to procedure '%-.64s'" est "Vigased parameetrid protseduurile '%-.64s'" - fre "Paramètre erroné pour la procedure %s" + fre "Paramètre erroné pour la procedure %-.64s" ger "Falsche Parameter für Prozedur '%-.64s'" greek "ËÜèïò ðáñÜìåôñïé óôçí äéáäéêáóßá '%-.64s'" hun "Rossz parameter a(z) '%-.64s' eljarasban" ita "Parametri errati per la procedura '%-.64s'" kor "'%-.64s' ¼öÇ๮¿¡ ´ëÇÑ ºÎÁ¤È®ÇÑ ÆĶó¸ÞÅÍ" - nor "Feil parametre til prosedyren %s" - norwegian-ny "Feil parameter til prosedyra %s" - pol "Incorrect parameters to procedure %s" + nor "Feil parametre til prosedyren %-.64s" + norwegian-ny "Feil parameter til prosedyra %-.64s" + pol "Incorrect parameters to procedure %-.64s" por "Parâmetros incorretos para a 'procedure' '%-.64s'" rum "Procedura '%-.64s' are parametrii incorecti" rus "îÅËÏÒÒÅËÔÎÙÅ ÐÁÒÁÍÅÔÒÙ ÄÌÑ ÐÒÏÃÅÄÕÒÙ '%-.64s'" serbian "Pogrešni parametri prosleðeni proceduri '%-.64s'" slo "Chybné parametre procedúry '%-.64s'" - spa "Equivocados parametros para procedimiento %s" - swe "Felaktiga parametrar till procedur %s" + spa "Equivocados parametros para procedimiento %-.64s" + swe "Felaktiga parametrar till procedur %-.64s" ukr "èÉÂÎÉÊ ÐÁÒÁÍÅÔÅÒ ÐÒÏÃÅÄÕÒÉ '%-.64s'" ER_UNKNOWN_TABLE 42S02 - cze "Nezn-Bámá tabulka '%-.64s' v %s" - dan "Ukendt tabel '%-.64s' i %s" - nla "Onbekende tabel '%-.64s' in %s" + cze "Nezn-Bámá tabulka '%-.64s' v %-.32s" + dan "Ukendt tabel '%-.64s' i %-.32s" + nla "Onbekende tabel '%-.64s' in %-.32s" eng "Unknown table '%-.64s' in %-.32s" est "Tundmatu tabel '%-.64s' %-.32s-s" - fre "Table inconnue '%-.64s' dans %s" - ger "Unbekannte Tabelle '%-.64s' in '%-.64s'" - greek "Áãíùóôïò ðßíáêáò '%-.64s' óå %s" - hun "Ismeretlen tabla: '%-.64s' %s-ban" - ita "Tabella '%-.64s' sconosciuta in %s" - jpn "Unknown table '%-.64s' in %s" - kor "¾Ë¼ö ¾ø´Â Å×À̺í '%-.64s' (µ¥ÀÌŸº£À̽º %s)" - nor "Ukjent tabell '%-.64s' i %s" - norwegian-ny "Ukjend tabell '%-.64s' i %s" - pol "Unknown table '%-.64s' in %s" + fre "Table inconnue '%-.64s' dans %-.32s" + ger "Unbekannte Tabelle '%-.64s' in '%-.32s'" + greek "Áãíùóôïò ðßíáêáò '%-.64s' óå %-.32s" + hun "Ismeretlen tabla: '%-.64s' %-.32s-ban" + ita "Tabella '%-.64s' sconosciuta in %-.32s" + jpn "Unknown table '%-.64s' in %-.32s" + kor "¾Ë¼ö ¾ø´Â Å×À̺í '%-.64s' (µ¥ÀÌŸº£À̽º %-.32s)" + nor "Ukjent tabell '%-.64s' i %-.32s" + norwegian-ny "Ukjend tabell '%-.64s' i %-.32s" + pol "Unknown table '%-.64s' in %-.32s" por "Tabela '%-.64s' desconhecida em '%-.32s'" rum "Tabla '%-.64s' invalida in %-.32s" rus "îÅÉÚ×ÅÓÔÎÁÑ ÔÁÂÌÉÃÁ '%-.64s' × %-.32s" serbian "Nepoznata tabela '%-.64s' u '%-.32s'" - slo "Neznáma tabuµka '%-.64s' v %s" - spa "Tabla desconocida '%-.64s' in %s" - swe "Okänd tabell '%-.64s' i '%-.64s'" + slo "Neznáma tabuµka '%-.64s' v %-.32s" + spa "Tabla desconocida '%-.64s' in %-.32s" + swe "Okänd tabell '%-.64s' i '%-.32s'" ukr "îÅצÄÏÍÁ ÔÁÂÌÉÃÑ '%-.64s' Õ %-.32s" ER_FIELD_SPECIFIED_TWICE 42000 cze "Polo-B¾ka '%-.64s' je zadána dvakrát" @@ -2803,27 +2803,27 @@ ER_TOO_MANY_FIELDS swe "För många fält" ukr "úÁÂÁÇÁÔÏ ÓÔÏ×Âæ×" ER_TOO_BIG_ROWSIZE 42000 - cze "-BØádek je pøíli¹ velký. Maximální velikost øádku, nepoèítaje polo¾ky blob, je %d. Musíte zmìnit nìkteré polo¾ky na blob" - dan "For store poster. Max post størrelse, uden BLOB's, er %d. Du må lave nogle felter til BLOB's" - nla "Rij-grootte is groter dan toegestaan. Maximale rij grootte, blobs niet meegeteld, is %d. U dient sommige velden in blobs te veranderen." + cze "-BØádek je pøíli¹ velký. Maximální velikost øádku, nepoèítaje polo¾ky blob, je %ld. Musíte zmìnit nìkteré polo¾ky na blob" + dan "For store poster. Max post størrelse, uden BLOB's, er %ld. Du må lave nogle felter til BLOB's" + nla "Rij-grootte is groter dan toegestaan. Maximale rij grootte, blobs niet meegeteld, is %ld. U dient sommige velden in blobs te veranderen." eng "Row size too large. The maximum row size for the used table type, not counting BLOBs, is %ld. You have to change some columns to TEXT or BLOBs" - jps "row size ‚ª‘å‚«‚·‚¬‚Ü‚·. BLOB ‚ðŠÜ‚Ü‚È‚¢ê‡‚Ì row size ‚ÌÅ‘å‚Í %d ‚Å‚·. ‚¢‚‚‚©‚Ì field ‚ð BLOB ‚É•Ï‚¦‚Ä‚‚¾‚³‚¢.", - est "Liiga pikk kirje. Kirje maksimumpikkus arvestamata BLOB-tüüpi välju on %d. Muuda mõned väljad BLOB-tüüpi väljadeks" - fre "Ligne trop grande. Le taille maximale d'une ligne, sauf les BLOBs, est %d. Changez le type de quelques colonnes en BLOB" + jps "row size ‚ª‘å‚«‚·‚¬‚Ü‚·. BLOB ‚ðŠÜ‚Ü‚È‚¢ê‡‚Ì row size ‚ÌÅ‘å‚Í %ld ‚Å‚·. ‚¢‚‚‚©‚Ì field ‚ð BLOB ‚É•Ï‚¦‚Ä‚‚¾‚³‚¢.", + est "Liiga pikk kirje. Kirje maksimumpikkus arvestamata BLOB-tüüpi välju on %ld. Muuda mõned väljad BLOB-tüüpi väljadeks" + fre "Ligne trop grande. Le taille maximale d'une ligne, sauf les BLOBs, est %ld. Changez le type de quelques colonnes en BLOB" ger "Zeilenlänge zu groß. Die maximale Zeilenlänge für den verwendeten Tabellentyp (ohne BLOB-Felder) beträgt %ld. Einige Felder müssen in BLOB oder TEXT umgewandelt werden" - greek "Ðïëý ìåãÜëï ìÝãåèïò åããñáöÞò. Ôï ìÝãéóôï ìÝãåèïò åããñáöÞò, ÷ùñßò íá õðïëïãßæïíôáé ôá blobs, åßíáé %d. ÐñÝðåé íá ïñßóåôå êÜðïéá ðåäßá óáí blobs" - hun "Tul nagy sormeret. A maximalis sormeret (nem szamolva a blob objektumokat) %d. Nehany mezot meg kell valtoztatnia" - ita "Riga troppo grande. La massima grandezza di una riga, non contando i BLOB, e` %d. Devi cambiare alcuni campi in BLOB" - jpn "row size ¤¬Â礤¹¤®¤Þ¤¹. BLOB ¤ò´Þ¤Þ¤Ê¤¤¾ì¹ç¤Î row size ¤ÎºÇÂç¤Ï %d ¤Ç¤¹. ¤¤¤¯¤Ä¤«¤Î field ¤ò BLOB ¤ËÊѤ¨¤Æ¤¯¤À¤µ¤¤." - kor "³Ê¹« Å« row »çÀÌÁîÀÔ´Ï´Ù. BLOB¸¦ °è»êÇÏÁö ¾Ê°í ÃÖ´ë row »çÀÌÁî´Â %dÀÔ´Ï´Ù. ¾ó¸¶°£ÀÇ ÇʵåµéÀ» BLOB·Î ¹Ù²Ù¼Å¾ß °Ú±º¿ä.." - por "Tamanho de linha grande demais. O máximo tamanho de linha, não contando BLOBs, é %d. Você tem que mudar alguns campos para BLOBs" - rum "Marimea liniei (row) prea mare. Marimea maxima a liniei, excluzind BLOB-urile este de %d. Trebuie sa schimbati unele cimpuri in BLOB-uri" - rus "óÌÉÛËÏÍ ÂÏÌØÛÏÊ ÒÁÚÍÅÒ ÚÁÐÉÓÉ. íÁËÓÉÍÁÌØÎÙÊ ÒÁÚÍÅÒ ÓÔÒÏËÉ, ÉÓËÌÀÞÁÑ ÐÏÌÑ BLOB, - %d. ÷ÏÚÍÏÖÎÏ, ×ÁÍ ÓÌÅÄÕÅÔ ÉÚÍÅÎÉÔØ ÔÉÐ ÎÅËÏÔÏÒÙÈ ÐÏÌÅÊ ÎÁ BLOB" - serbian "Prevelik slog. Maksimalna velièina sloga, ne raèunajuæi BLOB polja, je %d. Trebali bi da promenite tip nekih polja u BLOB" - slo "Riadok je príli¹ veµký. Maximálna veµkos» riadku, okrem 'BLOB', je %d. Musíte zmeni» niektoré polo¾ky na BLOB" - spa "Tamaño de línea muy grande. Máximo tamaño de línea, no contando blob, es %d. Tu tienes que cambiar algunos campos para blob" - swe "För stor total radlängd. Den högst tillåtna radlängden, förutom BLOBs, är %d. Ändra några av dina fält till BLOB" - ukr "úÁÄÏ×ÇÁ ÓÔÒÏËÁ. îÁʦÌØÛÏÀ ÄÏ×ÖÉÎÏÀ ÓÔÒÏËÉ, ÎÅ ÒÁÈÕÀÞÉ BLOB, ¤ %d. ÷ÁÍ ÐÏÔÒ¦ÂÎÏ ÐÒÉ×ÅÓÔÉ ÄÅÑ˦ ÓÔÏ×Âæ ÄÏ ÔÉÐÕ BLOB" + greek "Ðïëý ìåãÜëï ìÝãåèïò åããñáöÞò. Ôï ìÝãéóôï ìÝãåèïò åããñáöÞò, ÷ùñßò íá õðïëïãßæïíôáé ôá blobs, åßíáé %ld. ÐñÝðåé íá ïñßóåôå êÜðïéá ðåäßá óáí blobs" + hun "Tul nagy sormeret. A maximalis sormeret (nem szamolva a blob objektumokat) %ld. Nehany mezot meg kell valtoztatnia" + ita "Riga troppo grande. La massima grandezza di una riga, non contando i BLOB, e` %ld. Devi cambiare alcuni campi in BLOB" + jpn "row size ¤¬Â礤¹¤®¤Þ¤¹. BLOB ¤ò´Þ¤Þ¤Ê¤¤¾ì¹ç¤Î row size ¤ÎºÇÂç¤Ï %ld ¤Ç¤¹. ¤¤¤¯¤Ä¤«¤Î field ¤ò BLOB ¤ËÊѤ¨¤Æ¤¯¤À¤µ¤¤." + kor "³Ê¹« Å« row »çÀÌÁîÀÔ´Ï´Ù. BLOB¸¦ °è»êÇÏÁö ¾Ê°í ÃÖ´ë row »çÀÌÁî´Â %ldÀÔ´Ï´Ù. ¾ó¸¶°£ÀÇ ÇʵåµéÀ» BLOB·Î ¹Ù²Ù¼Å¾ß °Ú±º¿ä.." + por "Tamanho de linha grande demais. O máximo tamanho de linha, não contando BLOBs, é %ld. Você tem que mudar alguns campos para BLOBs" + rum "Marimea liniei (row) prea mare. Marimea maxima a liniei, excluzind BLOB-urile este de %ld. Trebuie sa schimbati unele cimpuri in BLOB-uri" + rus "óÌÉÛËÏÍ ÂÏÌØÛÏÊ ÒÁÚÍÅÒ ÚÁÐÉÓÉ. íÁËÓÉÍÁÌØÎÙÊ ÒÁÚÍÅÒ ÓÔÒÏËÉ, ÉÓËÌÀÞÁÑ ÐÏÌÑ BLOB, - %ld. ÷ÏÚÍÏÖÎÏ, ×ÁÍ ÓÌÅÄÕÅÔ ÉÚÍÅÎÉÔØ ÔÉÐ ÎÅËÏÔÏÒÙÈ ÐÏÌÅÊ ÎÁ BLOB" + serbian "Prevelik slog. Maksimalna velièina sloga, ne raèunajuæi BLOB polja, je %ld. Trebali bi da promenite tip nekih polja u BLOB" + slo "Riadok je príli¹ veµký. Maximálna veµkos» riadku, okrem 'BLOB', je %ld. Musíte zmeni» niektoré polo¾ky na BLOB" + spa "Tamaño de línea muy grande. Máximo tamaño de línea, no contando blob, es %ld. Tu tienes que cambiar algunos campos para blob" + swe "För stor total radlängd. Den högst tillåtna radlängden, förutom BLOBs, är %ld. Ändra några av dina fält till BLOB" + ukr "úÁÄÏ×ÇÁ ÓÔÒÏËÁ. îÁʦÌØÛÏÀ ÄÏ×ÖÉÎÏÀ ÓÔÒÏËÉ, ÎÅ ÒÁÈÕÀÞÉ BLOB, ¤ %ld. ÷ÁÍ ÐÏÔÒ¦ÂÎÏ ÐÒÉ×ÅÓÔÉ ÄÅÑ˦ ÓÔÏ×Âæ ÄÏ ÔÉÐÕ BLOB" ER_STACK_OVERRUN cze "P-Bøeteèení zásobníku threadu: pou¾ito %ld z %ld. Pou¾ijte 'mysqld -O thread_stack=#' k zadání vìt¹ího zásobníku" dan "Thread stack brugt: Brugt: %ld af en %ld stak. Brug 'mysqld -O thread_stack=#' for at allokere en større stak om nødvendigt" @@ -3026,14 +3026,14 @@ ER_FUNCTION_NOT_DEFINED ukr "æÕÎËæÀ '%-.64s' ÎÅ ×ÉÚÎÁÞÅÎÏ" ER_HOST_IS_BLOCKED cze "Stroj '%-.64s' je zablokov-Bán kvùli mnoha chybám pøi pøipojování. Odblokujete pou¾itím 'mysqladmin flush-hosts'" - dan "Værten er blokeret på grund af mange fejlforespørgsler. Lås op med 'mysqladmin flush-hosts'" + dan "Værten '%-.64s' er blokeret på grund af mange fejlforespørgsler. Lås op med 'mysqladmin flush-hosts'" nla "Host '%-.64s' is geblokkeeerd vanwege te veel verbindings fouten. Deblokkeer met 'mysqladmin flush-hosts'" eng "Host '%-.64s' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'" jps "Host '%-.64s' ‚Í many connection error ‚Ì‚½‚ßA‹‘”Û‚³‚ê‚Ü‚µ‚½. 'mysqladmin flush-hosts' ‚ʼn𜂵‚Ä‚‚¾‚³‚¢", est "Masin '%-.64s' on blokeeritud hulgaliste ühendusvigade tõttu. Blokeeringu saab tühistada 'mysqladmin flush-hosts' käsuga" - fre "L'hôte '%-.64s' est bloqué à cause d'un trop grand nombre d'erreur de connection. Débloquer le par 'mysqladmin flush-hosts'" + fre "L'hôte '%-.64s' est bloqué à cause d'un trop grand nombre d'erreur de connexion. Débloquer le par 'mysqladmin flush-hosts'" ger "Host '%-.64s' blockiert wegen zu vieler Verbindungsfehler. Aufheben der Blockierung mit 'mysqladmin flush-hosts'" - greek "Ï õðïëïãéóôÞò Ý÷åé áðïêëåéóèåß ëüãù ðïëëáðëþí ëáèþí óýíäåóçò. ÐñïóðáèÞóôå íá äéïñþóåôå ìå 'mysqladmin flush-hosts'" + greek "Ï õðïëïãéóôÞò '%-.64s' Ý÷åé áðïêëåéóèåß ëüãù ðïëëáðëþí ëáèþí óýíäåóçò. ÐñïóðáèÞóôå íá äéïñþóåôå ìå 'mysqladmin flush-hosts'" hun "A '%-.64s' host blokkolodott, tul sok kapcsolodasi hiba miatt. Hasznalja a 'mysqladmin flush-hosts' parancsot" ita "Sistema '%-.64s' bloccato a causa di troppi errori di connessione. Per sbloccarlo: 'mysqladmin flush-hosts'" jpn "Host '%-.64s' ¤Ï many connection error ¤Î¤¿¤á¡¢µñÈݤµ¤ì¤Þ¤·¤¿. 'mysqladmin flush-hosts' ¤Ç²ò½ü¤·¤Æ¤¯¤À¤µ¤¤" @@ -3054,7 +3054,7 @@ ER_HOST_NOT_PRIVILEGED est "Masinal '%-.64s' puudub ligipääs sellele MySQL serverile" fre "Le hôte '%-.64s' n'est pas authorisé à se connecter à ce serveur MySQL" ger "Host '%-.64s' hat keine Berechtigung, sich mit diesem MySQL-Server zu verbinden" - greek "Ï õðïëïãéóôÞò äåí Ý÷åé äéêáßùìá óýíäåóçò ìå ôïí MySQL server" + greek "Ï õðïëïãéóôÞò '%-.64s' äåí Ý÷åé äéêáßùìá óýíäåóçò ìå ôïí MySQL server" hun "A '%-.64s' host szamara nem engedelyezett a kapcsolodas ehhez a MySQL szerverhez" ita "Al sistema '%-.64s' non e` consentita la connessione a questo server MySQL" jpn "Host '%-.64s' ¤Ï MySQL server ¤ËÀܳ¤òµö²Ä¤µ¤ì¤Æ¤¤¤Þ¤»¤ó" @@ -3368,27 +3368,27 @@ ER_GRANT_WRONG_HOST_OR_USER 42000 swe "Felaktigt maskinnamn eller användarnamn använt med GRANT" ukr "áÒÇÕÍÅÎÔ host ÁÂÏ user ÄÌÑ GRANT ÚÁÄÏ×ÇÉÊ" ER_NO_SUCH_TABLE 42S02 - cze "Tabulka '%-.64s.%s' neexistuje" + cze "Tabulka '%-.64s.%-.64s' neexistuje" dan "Tabellen '%-.64s.%-.64s' eksisterer ikke" - nla "Tabel '%-.64s.%s' bestaat niet" + nla "Tabel '%-.64s.%-.64s' bestaat niet" eng "Table '%-.64s.%-.64s' doesn't exist" est "Tabelit '%-.64s.%-.64s' ei eksisteeri" - fre "La table '%-.64s.%s' n'existe pas" + fre "La table '%-.64s.%-.64s' n'existe pas" ger "Tabelle '%-.64s.%-.64s' existiert nicht" - hun "A '%-.64s.%s' tabla nem letezik" - ita "La tabella '%-.64s.%s' non esiste" - jpn "Table '%-.64s.%s' doesn't exist" - kor "Å×À̺í '%-.64s.%s' ´Â Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù." - nor "Table '%-.64s.%s' doesn't exist" - norwegian-ny "Table '%-.64s.%s' doesn't exist" - pol "Table '%-.64s.%s' doesn't exist" + hun "A '%-.64s.%-.64s' tabla nem letezik" + ita "La tabella '%-.64s.%-.64s' non esiste" + jpn "Table '%-.64s.%-.64s' doesn't exist" + kor "Å×À̺í '%-.64s.%-.64s' ´Â Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù." + nor "Table '%-.64s.%-.64s' doesn't exist" + norwegian-ny "Table '%-.64s.%-.64s' doesn't exist" + pol "Table '%-.64s.%-.64s' doesn't exist" por "Tabela '%-.64s.%-.64s' não existe" rum "Tabela '%-.64s.%-.64s' nu exista" rus "ôÁÂÌÉÃÁ '%-.64s.%-.64s' ÎÅ ÓÕÝÅÓÔ×ÕÅÔ" serbian "Tabela '%-.64s.%-.64s' ne postoji" - slo "Table '%-.64s.%s' doesn't exist" - spa "Tabla '%-.64s.%s' no existe" - swe "Det finns ingen tabell som heter '%-.64s.%s'" + slo "Table '%-.64s.%-.64s' doesn't exist" + spa "Tabla '%-.64s.%-.64s' no existe" + swe "Det finns ingen tabell som heter '%-.64s.%-.64s'" ukr "ôÁÂÌÉÃÑ '%-.64s.%-.64s' ÎÅ ¦ÓÎÕ¤" ER_NONEXISTING_TABLE_GRANT 42000 cze "Neexistuje odpov-Bídající grant pro u¾ivatele '%-.32s' na stroji '%-.64s' pro tabulku '%-.64s'" @@ -3487,27 +3487,27 @@ ER_TOO_MANY_DELAYED_THREADS swe "Det finns redan 'max_delayed_threads' trådar i använding" ukr "úÁÂÁÇÁÔÏ ÚÁÔÒÉÍÁÎÉÈ Ç¦ÌÏË ×ÉËÏÒÉÓÔÏ×Õ¤ÔØÓÑ" ER_ABORTING_CONNECTION 08S01 - cze "Zru-B¹eno spojení %ld do databáze: '%-.64s' u¾ivatel: '%-.64s' (%s)" - dan "Afbrudt forbindelse %ld til database: '%-.64s' bruger: '%-.64s' (%-.64s)" - nla "Afgebroken verbinding %ld naar db: '%-.64s' gebruiker: '%-.64s' (%s)" + cze "Zru-B¹eno spojení %ld do databáze: '%-.64s' u¾ivatel: '%-.32s' (%-.64s)" + dan "Afbrudt forbindelse %ld til database: '%-.64s' bruger: '%-.32s' (%-.64s)" + nla "Afgebroken verbinding %ld naar db: '%-.64s' gebruiker: '%-.32s' (%-.64s)" eng "Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s)" est "Ühendus katkestatud %ld andmebaasile: '%-.64s' kasutajale: '%-.32s' (%-.64s)" - fre "Connection %ld avortée vers la bd: '%-.64s' utilisateur: '%-.64s' (%s)" - ger "Abbruch der Verbindung %ld zur Datenbank '%-.64s'. Benutzer: '%-.64s' (%-.64s)" - hun "Megszakitott kapcsolat %ld db: '%-.64s' adatbazishoz, felhasznalo: '%-.64s' (%s)" - ita "Interrotta la connessione %ld al db: '%-.64s' utente: '%-.64s' (%s)" - jpn "Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)" - kor "µ¥ÀÌŸº£À̽º Á¢¼ÓÀ» À§ÇÑ ¿¬°á %ld°¡ Áß´ÜµÊ : '%-.64s' »ç¿ëÀÚ: '%-.64s' (%s)" - nor "Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)" - norwegian-ny "Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)" - pol "Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)" + fre "Connection %ld avortée vers la bd: '%-.64s' utilisateur: '%-.32s' (%-.64s)" + ger "Abbruch der Verbindung %ld zur Datenbank '%-.64s'. Benutzer: '%-.32s' (%-.64s)" + hun "Megszakitott kapcsolat %ld db: '%-.64s' adatbazishoz, felhasznalo: '%-.32s' (%-.64s)" + ita "Interrotta la connessione %ld al db: '%-.64s' utente: '%-.32s' (%-.64s)" + jpn "Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s)" + kor "µ¥ÀÌŸº£À̽º Á¢¼ÓÀ» À§ÇÑ ¿¬°á %ld°¡ Áß´ÜµÊ : '%-.64s' »ç¿ëÀÚ: '%-.32s' (%-.64s)" + nor "Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s)" + norwegian-ny "Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s)" + pol "Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s)" por "Conexão %ld abortou para o banco de dados '%-.64s' - usuário '%-.32s' (%-.64s)" rum "Conectie terminata %ld la baza de date: '%-.64s' utilizator: '%-.32s' (%-.64s)" rus "ðÒÅÒ×ÁÎÏ ÓÏÅÄÉÎÅÎÉÅ %ld Ë ÂÁÚÅ ÄÁÎÎÙÈ '%-.64s' ÐÏÌØÚÏ×ÁÔÅÌÑ '%-.32s' (%-.64s)" serbian "Prekinuta konekcija broj %ld ka bazi: '%-.64s' korisnik je bio: '%-.32s' (%-.64s)" - slo "Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)" - spa "Conexión abortada %ld para db: '%-.64s' usuario: '%-.64s' (%s)" - swe "Avbröt länken för tråd %ld till db '%-.64s', användare '%-.64s' (%s)" + slo "Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s)" + spa "Conexión abortada %ld para db: '%-.64s' usuario: '%-.32s' (%-.64s)" + swe "Avbröt länken för tråd %ld till db '%-.64s', användare '%-.32s' (%-.64s)" ukr "ðÅÒÅÒ×ÁÎÏ Ú'¤ÄÎÁÎÎÑ %ld ÄÏ ÂÁÚÉ ÄÁÎÎÉÈ: '%-.64s' ËÏÒÉÓÔÕ×ÁÞÁ: '%-.32s' (%-.64s)" ER_NET_PACKET_TOO_LARGE 08S01 cze "Zji-B¹tìn pøíchozí packet del¹í ne¾ 'max_allowed_packet'" @@ -3533,7 +3533,7 @@ ER_NET_READ_ERROR_FROM_PIPE 08S01 nla "Kreeg leesfout van de verbindings pipe" eng "Got a read error from the connection pipe" est "Viga ühendustoru lugemisel" - fre "Erreur de lecture reçue du pipe de connection" + fre "Erreur de lecture reçue du pipe de connexion" ger "Lese-Fehler bei einer Verbindungs-Pipe" hun "Olvasasi hiba a kapcsolat soran" ita "Rilevato un errore di lettura dalla pipe di connessione" @@ -3986,7 +3986,7 @@ ER_CHECK_NOT_IMPLEMENTED 42000 por "O manipulador de tabela não suporta %s" rum "The handler for the table doesn't support %s" rus "ïÂÒÁÂÏÔÞÉË ÔÁÂÌÉÃÙ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÜÔÏÇÏ: %s" - serbian "Handler za ovu tabelu ne dozvoljava 'check' odnosno 'repair' komande" + serbian "Handler za ovu tabelu ne dozvoljava %s komande" slo "The handler for the table doesn't support %s" spa "El manipulador de la tabla no permite soporte para %s" swe "Tabellhanteraren för denna tabell kan inte göra %s" @@ -4331,7 +4331,7 @@ ER_TOO_MANY_USER_CONNECTIONS 42000 nla "Gebruiker %-.64s heeft reeds meer dan 'max_user_connections' actieve verbindingen" eng "User %-.64s already has more than 'max_user_connections' active connections" est "Kasutajal %-.64s on juba rohkem ühendusi kui lubatud 'max_user_connections' muutujaga" - fre "L'utilisateur %-.64s possède déjà plus de 'max_user_connections' connections actives" + fre "L'utilisateur %-.64s possède déjà plus de 'max_user_connections' connexions actives" ger "Benutzer '%-.64s' hat mehr als 'max_user_connections' aktive Verbindungen" ita "L'utente %-.64s ha gia' piu' di 'max_user_connections' connessioni attive" por "Usuário '%-.64s' já possui mais que o valor máximo de conexões (max_user_connections) ativas" @@ -4654,14 +4654,14 @@ ER_NO_DEFAULT 42000 spa "Variable '%-.64s' no tiene un valor patrón" swe "Variabel '%-.64s' har inte ett DEFAULT-värde" ER_WRONG_VALUE_FOR_VAR 42000 - nla "Variabele '%-.64s' kan niet worden gewijzigd naar de waarde '%-.64s'" - eng "Variable '%-.64s' can't be set to the value of '%-.64s'" - ger "Variable '%-.64s' kann nicht auf '%-.64s' gesetzt werden" - ita "Alla variabile '%-.64s' non puo' essere assegato il valore '%-.64s'" - por "Variável '%-.64s' não pode ser configurada para o valor de '%-.64s'" - rus "ðÅÒÅÍÅÎÎÁÑ '%-.64s' ÎÅ ÍÏÖÅÔ ÂÙÔØ ÕÓÔÁÎÏ×ÌÅÎÁ × ÚÎÁÞÅÎÉÅ '%-.64s'" - spa "Variable '%-.64s' no puede ser configurada para el valor de '%-.64s'" - swe "Variabel '%-.64s' kan inte sättas till '%-.64s'" + nla "Variabele '%-.64s' kan niet worden gewijzigd naar de waarde '%-.200s'" + eng "Variable '%-.64s' can't be set to the value of '%-.200s'" + ger "Variable '%-.64s' kann nicht auf '%-.200s' gesetzt werden" + ita "Alla variabile '%-.64s' non puo' essere assegato il valore '%-.200s'" + por "Variável '%-.64s' não pode ser configurada para o valor de '%-.200s'" + rus "ðÅÒÅÍÅÎÎÁÑ '%-.64s' ÎÅ ÍÏÖÅÔ ÂÙÔØ ÕÓÔÁÎÏ×ÌÅÎÁ × ÚÎÁÞÅÎÉÅ '%-.200s'" + spa "Variable '%-.64s' no puede ser configurada para el valor de '%-.200s'" + swe "Variabel '%-.64s' kan inte sättas till '%-.200s'" ER_WRONG_TYPE_FOR_VAR 42000 nla "Foutief argumenttype voor variabele '%-.64s'" eng "Incorrect argument type to variable '%-.64s'" @@ -5220,9 +5220,9 @@ ER_FPARSER_BAD_HEADER ukr "îÅצÒÎÉÊ ÚÁÇÏÌÏ×ÏË ÔÉÐÕ Õ ÆÁÊ̦ '%-.64s'" ER_FPARSER_EOF_IN_COMMENT eng "Unexpected end of file while parsing comment '%-.200s'" - ger "Unerwartetes Dateiende beim Parsen des Kommentars '%-.64s'" - rus "îÅÏÖÉÄÁÎÎÙÊ ËÏÎÅà ÆÁÊÌÁ × ËÏÍÅÎÔÁÒÉÉ '%-.64s'" - ukr "îÅÓÐÏĦ×ÁÎÎÉÊ Ë¦ÎÅÃØ ÆÁÊÌÕ Õ ËÏÍÅÎÔÁÒ¦ '%-.64s'" + ger "Unerwartetes Dateiende beim Parsen des Kommentars '%-.200s'" + rus "îÅÏÖÉÄÁÎÎÙÊ ËÏÎÅà ÆÁÊÌÁ × ËÏÍÅÎÔÁÒÉÉ '%-.200s'" + ukr "îÅÓÐÏĦ×ÁÎÎÉÊ Ë¦ÎÅÃØ ÆÁÊÌÕ Õ ËÏÍÅÎÔÁÒ¦ '%-.200s'" ER_FPARSER_ERROR_IN_PARAMETER eng "Error while parsing parameter '%-.64s' (line: '%-.64s')" ger "Fehler beim Parsen des Parameters '%-.64s' (Zeile: '%-.64s')" @@ -5307,7 +5307,7 @@ ER_TRG_ON_VIEW_OR_TEMP_TABLE ger "'%-.64s' des Triggers ist View oder temporäre Tabelle" ER_TRG_CANT_CHANGE_ROW eng "Updating of %s row is not allowed in %strigger" - ger "Aktualisieren einer %s-Zeile ist in einem %-Trigger nicht erlaubt" + ger "Aktualisieren einer %s-Zeile ist in einem %s-Trigger nicht erlaubt" ER_TRG_NO_SUCH_ROW_IN_TRG eng "There is no %s row in %s trigger" ger "Es gibt keine %s-Zeile im %s-Trigger" @@ -5389,7 +5389,7 @@ ER_LOGGING_PROHIBIT_CHANGING_OF ger "Binärlogs und Replikation verhindern Wechsel des globalen Servers %s" ER_NO_FILE_MAPPING eng "Can't map file: %-.200s, errno: %d" - ger "Kann Datei nicht abbilden: %-.64s, Fehler: %d" + ger "Kann Datei nicht abbilden: %-.200s, Fehler: %d" ER_WRONG_MAGIC eng "Wrong magic in %-.64s" ger "Falsche magische Zahlen in %-.64s" @@ -5524,7 +5524,7 @@ ER_CONNECT_TO_FOREIGN_DATA_SOURCE eng "Unable to connect to foreign data source: %.64s" ger "Kann nicht mit Fremddatenquelle verbinden: %.64s" ER_QUERY_ON_FOREIGN_DATA_SOURCE - eng "There was a problem processing the query on the foreign data source. Data source error: %-.64" + eng "There was a problem processing the query on the foreign data source. Data source error: %-.64s" ger "Bei der Verarbeitung der Abfrage ist in der Fremddatenquelle ein Problem aufgetreten. Datenquellenfehlermeldung: %-.64s" ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST eng "The foreign data source you are trying to reference does not exist. Data source error: %-.64s" @@ -5606,7 +5606,9 @@ ER_SP_RECURSION_LIMIT ger "Rekursionsgrenze %d (durch Variable max_sp_recursion_depth gegeben) wurde für Routine %.64s überschritten" ER_SP_PROC_TABLE_CORRUPT eng "Failed to load routine %-.64s. The table mysql.proc is missing, corrupt, or contains bad data (internal code %d)" - ger "Routine %-64s konnte nicht geladen werden. Die Tabelle mysql.proc fehlt, ist beschädigt, oder enthält fehlerhaften Daten (interner Code: %d)" + ger "Routine %-.64s konnte nicht geladen werden. Die Tabelle mysql.proc fehlt, ist beschädigt, oder enthält fehlerhaften Daten (interner Code: %d)" +ER_FOREIGN_SERVER_EXISTS + eng "The foreign server, %s, you are trying to create already exists." ER_SP_WRONG_NAME 42000 eng "Incorrect routine name '%-.64s'" ger "Ungültiger Routinenname '%-.64s'" @@ -5847,6 +5849,9 @@ ER_BINLOG_ROW_WRONG_TABLE_DEF ER_BINLOG_ROW_RBR_TO_SBR eng "Slave running with --log-slave-updates must use row-based binary logging to be able to replicate row-based binary log events" ger "Slave, die mit --log-slave-updates laufen, müssen zeilenbasiertes Loggen verwenden, um zeilenbasierte Binärlog-Ereignisse loggen zu können" +ER_FOREIGN_SERVER_DOESNT_EXIST + eng "The foreign server name you are trying to reference does not exist. Data source error: %-.64s" + ger "Die externe Verbindung, auf die Sie zugreifen wollen, existiert nicht. Datenquellenfehlermeldung: %-.64s" ER_EVENT_ALREADY_EXISTS eng "Event '%-.64s' already exists" ger "Event '%-.64s' existiert bereits" @@ -6004,4 +6009,47 @@ ER_BAD_LOG_STATEMENT ger "Sie können eine Logtabelle nicht '%s', wenn Loggen angeschaltet ist" ER_NON_INSERTABLE_TABLE eng "The target table %-.100s of the %s is not insertable-into" - + ger "Die Zieltabelle %-.100s von %s ist nicht einfügbar" +ER_CANT_RENAME_LOG_TABLE + eng "Cannot rename '%s'. When logging enabled, rename to/from log table must rename two tables: the log table to an archive table and another table back to '%s'" + ger "Kann '%s' nicht umbenennen. Wenn Loggen angeschaltet ist, müssen beim Umbenennen zu/von einer Logtabelle zwei Tabellen angegeben werden: die Logtabelle zu einer Archivtabelle und eine weitere Tabelle zurück zu '%s'" +ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT 42000 + eng "Incorrect parameter count in the call to native function '%-.64s'" + ger "Falsche Anzahl von Parametern beim Aufruf der nativen Funktion '%-.64s'" +ER_WRONG_PARAMETERS_TO_NATIVE_FCT 42000 + eng "Incorrect parameters in the call to native function '%-.64s'" + ger "Falscher Parameter beim Aufruf der nativen Funktion '%-.64s'" +ER_WRONG_PARAMETERS_TO_STORED_FCT 42000 + eng "Incorrect parameters in the call to stored function '%-.64s'" + ger "Falsche Parameter beim Aufruf der gespeicherten Funktion '%-.64s'" +ER_NATIVE_FCT_NAME_COLLISION + eng "This function '%-.64s' has the same name as a native function" + ger "Die Funktion '%-.64s' hat denselben Namen wie eine native Funktion" +ER_DUP_ENTRY_WITH_KEY_NAME 23000 S1009 + cze "Zvojen-Bý klíè '%-.64s' (èíslo klíèe '%-.64s')" + dan "Ens værdier '%-.64s' for indeks '%-.64s'" + nla "Dubbele ingang '%-.64s' voor zoeksleutel '%-.64s'" + eng "Duplicate entry '%-.64s' for key '%-.64s'" + jps "'%-.64s' ‚Í key '%-.64s' ‚É‚¨‚¢‚Äd•¡‚µ‚Ä‚¢‚Ü‚·", + est "Kattuv väärtus '%-.64s' võtmele '%-.64s'" + fre "Duplicata du champ '%-.64s' pour la clef '%-.64s'" + ger "Doppelter Eintrag '%-.64s' für Schlüssel '%-.64s'" + greek "ÄéðëÞ åããñáöÞ '%-.64s' ãéá ôï êëåéäß '%-.64s'" + hun "Duplikalt bejegyzes '%-.64s' a '%-.64s' kulcs szerint." + ita "Valore duplicato '%-.64s' per la chiave '%-.64s'" + jpn "'%-.64s' ¤Ï key '%-.64s' ¤Ë¤ª¤¤¤Æ½ÅÊ£¤·¤Æ¤¤¤Þ¤¹" + kor "Áߺ¹µÈ ÀÔ·Â °ª '%-.64s': key '%-.64s'" + nor "Like verdier '%-.64s' for nøkkel '%-.64s'" + norwegian-ny "Like verdiar '%-.64s' for nykkel '%-.64s'" + pol "Powtórzone wyst?pienie '%-.64s' dla klucza '%-.64s'" + por "Entrada '%-.64s' duplicada para a chave '%-.64s'" + rum "Cimpul '%-.64s' e duplicat pentru cheia '%-.64s'" + rus "äÕÂÌÉÒÕÀÝÁÑÓÑ ÚÁÐÉÓØ '%-.64s' ÐÏ ËÌÀÞÕ '%-.64s'" + serbian "Dupliran unos '%-.64s' za kljuè '%-.64s'" + slo "Opakovaný kµúè '%-.64s' (èíslo kµúèa '%-.64s')" + spa "Entrada duplicada '%-.64s' para la clave '%-.64s'" + swe "Dubbel nyckel '%-.64s' för nyckel '%-.64s'" + ukr "äÕÂÌÀÀÞÉÊ ÚÁÐÉÓ '%-.64s' ÄÌÑ ËÌÀÞÁ '%-.64s'" +ER_BINLOG_PURGE_EMFILE + eng "Too many files opened, please execute the command again" + ger "Zu viele offene Dateien, bitte führen Sie den Befehl noch einmal aus" diff --git a/sql/slave.cc b/sql/slave.cc index 0e705d732ea..278edae99f4 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,6 +32,7 @@ int queue_event(MASTER_INFO* mi,const char* buf,ulong event_len); +#define FLAGSTR(V,F) ((V)&(F)?#F" ":"") #define MAX_SLAVE_RETRY_PAUSE 5 bool use_slave_mask = 0; @@ -53,6 +53,7 @@ ulonglong relay_log_space_limit = 0; */ int disconnect_slave_event_count = 0, abort_slave_event_count = 0; +int events_till_abort = -1; typedef enum { SLAVE_THD_IO, SLAVE_THD_SQL} SLAVE_THD_TYPE; @@ -73,6 +74,7 @@ static int request_table_dump(MYSQL* mysql, const char* db, const char* table); static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, const char* table_name, bool overwrite); static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi); +static Log_event* next_event(RELAY_LOG_INFO* rli); /* Find out which replications threads are running @@ -913,7 +915,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, TABLE_LIST tables; int error= 1; handler *file; - ulong save_options; + ulonglong save_options; NET *net= &mysql->net; DBUG_ENTER("create_table_from_dump"); @@ -1295,7 +1297,7 @@ bool show_master_info(THD* thd, MASTER_INFO* mi) rpl_filter->get_wild_ignore_table(&tmp); protocol->store(&tmp); - protocol->store((uint32) mi->rli.last_slave_errno); + protocol->store(mi->rli.last_slave_errno); protocol->store(mi->rli.last_slave_error, &my_charset_bin); protocol->store((uint32) mi->rli.slave_skip_counter); protocol->store((ulonglong) mi->rli.group_master_log_pos); @@ -1326,12 +1328,12 @@ bool show_master_info(THD* thd, MASTER_INFO* mi) if ((mi->slave_running == MYSQL_SLAVE_RUN_CONNECT) && mi->rli.slave_running) { - long tmp= (long)((time_t)time((time_t*) 0) - - mi->rli.last_master_timestamp) - - mi->clock_diff_with_master; + long time_diff= ((long)((time_t)time((time_t*) 0) + - mi->rli.last_master_timestamp) + - mi->clock_diff_with_master); /* - Apparently on some systems tmp can be <0. Here are possible reasons - related to MySQL: + Apparently on some systems time_diff can be <0. Here are possible + reasons related to MySQL: - the master is itself a slave of another master whose time is ahead. - somebody used an explicit SET TIMESTAMP on the master. Possible reason related to granularity-to-second of time functions @@ -1349,8 +1351,8 @@ bool show_master_info(THD* thd, MASTER_INFO* mi) last_master_timestamp == 0 (an "impossible" timestamp 1970) is a special marker to say "consider we have caught up". */ - protocol->store((longlong)(mi->rli.last_master_timestamp ? max(0, tmp) - : 0)); + protocol->store((longlong)(mi->rli.last_master_timestamp ? + max(0, time_diff) : 0)); } else protocol->store_null(); @@ -1369,9 +1371,21 @@ bool show_master_info(THD* thd, MASTER_INFO* mi) void set_slave_thread_options(THD* thd) { DBUG_ENTER("set_slave_thread_options"); - - thd->options = ((opt_log_slave_updates) ? OPTION_BIN_LOG:0) | - OPTION_AUTO_IS_NULL; + /* + It's nonsense to constrain the slave threads with max_join_size; if a + query succeeded on master, we HAVE to execute it. So set + OPTION_BIG_SELECTS. Setting max_join_size to HA_POS_ERROR is not enough + (and it's not needed if we have OPTION_BIG_SELECTS) because an INSERT + SELECT examining more than 4 billion rows would still fail (yes, because + when max_join_size is 4G, OPTION_BIG_SELECTS is automatically set, but + only for client threads. + */ + ulonglong options= thd->options | OPTION_BIG_SELECTS; + if (opt_log_slave_updates) + options|= OPTION_BIN_LOG; + else + options&= ~OPTION_BIN_LOG; + thd->options= options; thd->variables.completion_type= 0; DBUG_VOID_RETURN; } @@ -1409,24 +1423,18 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type) SYSTEM_THREAD_SLAVE_SQL : SYSTEM_THREAD_SLAVE_IO; thd->security_ctx->skip_grants(); my_net_init(&thd->net, 0); - thd->net.read_timeout = slave_net_timeout; +/* + Adding MAX_LOG_EVENT_HEADER_LEN to the max_allowed_packet on all + slave threads, since a replication event can become this much larger + than the corresponding packet (query) sent from client to master. +*/ + thd->variables.max_allowed_packet= global_system_variables.max_allowed_packet + + MAX_LOG_EVENT_HEADER; /* note, incr over the global not session var */ thd->slave_thread = 1; set_slave_thread_options(thd); - /* - It's nonsense to constrain the slave threads with max_join_size; if a - query succeeded on master, we HAVE to execute it. So set - OPTION_BIG_SELECTS. Setting max_join_size to HA_POS_ERROR is not enough - (and it's not needed if we have OPTION_BIG_SELECTS) because an INSERT - SELECT examining more than 4 billion rows would still fail (yes, because - when max_join_size is 4G, OPTION_BIG_SELECTS is automatically set, but - only for client threads. - */ - thd->options = ((opt_log_slave_updates) ? OPTION_BIN_LOG:0) | - OPTION_AUTO_IS_NULL | OPTION_BIG_SELECTS; thd->client_capabilities = CLIENT_LOCAL_FILES; - thd->real_id=pthread_self(); pthread_mutex_lock(&LOCK_thread_count); - thd->thread_id = thread_id++; + thd->thread_id= thd->variables.pseudo_thread_id= thread_id++; pthread_mutex_unlock(&LOCK_thread_count); if (init_thr_lock() || thd->store_globals()) @@ -1436,12 +1444,6 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type) DBUG_RETURN(-1); } -#if !defined(__WIN__) && !defined(__NETWARE__) - sigset_t set; - VOID(sigemptyset(&set)); // Get mask in use - VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); -#endif - if (thd_type == SLAVE_THD_SQL) thd->proc_info= "Waiting for the next event in relay log"; else @@ -1608,7 +1610,7 @@ static ulong read_event(MYSQL* mysql, MASTER_INFO *mi, bool* suppress_warnings) DBUG_RETURN(packet_error); } - DBUG_PRINT("info",( "len=%u, net->read_pos[4] = %d\n", + DBUG_PRINT("exit", ("len: %lu net->read_pos[4]: %d", len, mysql->net.read_pos[4])); DBUG_RETURN(len - 1); } @@ -1728,6 +1730,10 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) DBUG_PRINT("info",("type_code=%d (%s), server_id=%d", type_code, ev->get_type_str(), ev->server_id)); + DBUG_PRINT("info", ("thd->options={ %s%s}", + FLAGSTR(thd->options, OPTION_NOT_AUTOCOMMIT), + FLAGSTR(thd->options, OPTION_BEGIN))); + /* @@ -1965,11 +1971,19 @@ pthread_handler_t handle_slave_io(void *arg) thd->proc_info = "Connecting to master"; // we can get killed during safe_connect if (!safe_connect(thd, mysql, mi)) - sql_print_information("Slave I/O thread: connected to master '%s@%s:%d',\ - replication started in log '%s' at position %s", mi->user, - mi->host, mi->port, - IO_RPL_LOG_NAME, - llstr(mi->master_log_pos,llbuff)); + { + sql_print_information("Slave I/O thread: connected to master '%s@%s:%d'," + "replication started in log '%s' at position %s", + mi->user, mi->host, mi->port, + IO_RPL_LOG_NAME, + llstr(mi->master_log_pos,llbuff)); + /* + Adding MAX_LOG_EVENT_HEADER_LEN to the max_packet_size on the I/O + thread, since a replication event can become this much larger than + the corresponding packet (query) sent from client to master. + */ + mysql->net.max_packet_size= thd->net.max_packet_size+= MAX_LOG_EVENT_HEADER; + } else { sql_print_information("Slave I/O thread killed while connecting to master"); @@ -2055,15 +2069,16 @@ after reconnect"); while (!io_slave_killed(thd,mi)) { - bool suppress_warnings= 0; + ulong event_len; + suppress_warnings= 0; /* We say "waiting" because read_event() will wait if there's nothing to read. But if there's something to read, it will not wait. The important thing is to not confuse users by saying "reading" whereas we're in fact receiving nothing. */ - thd->proc_info = "Waiting for master to send event"; - ulong event_len = read_event(mysql, mi, &suppress_warnings); + thd->proc_info= "Waiting for master to send event"; + event_len= read_event(mysql, mi, &suppress_warnings); if (io_slave_killed(thd,mi)) { if (global_system_variables.log_warnings) @@ -2214,11 +2229,16 @@ err: THD_CHECK_SENTRY(thd); delete thd; pthread_mutex_unlock(&LOCK_thread_count); - mi->abort_slave = 0; - mi->slave_running = 0; - mi->io_thd = 0; - pthread_mutex_unlock(&mi->run_lock); + mi->abort_slave= 0; + mi->slave_running= 0; + mi->io_thd= 0; + /* + Note: the order of the two following calls (first broadcast, then unlock) + is important. Otherwise a killer_thread can execute between the calls and + delete the mi structure leading to a crash! (see BUG#25306 for details) + */ pthread_cond_broadcast(&mi->stop_cond); // tell the world we are done + pthread_mutex_unlock(&mi->run_lock); my_thread_end(); pthread_exit(0); DBUG_RETURN(0); // Can't return anything here @@ -2468,9 +2488,14 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ THD_CHECK_SENTRY(thd); delete thd; pthread_mutex_unlock(&LOCK_thread_count); + /* + Note: the order of the broadcast and unlock calls below (first broadcast, then unlock) + is important. Otherwise a killer_thread can execute between the calls and + delete the mi structure leading to a crash! (see BUG#25306 for details) + */ pthread_cond_broadcast(&rli->stop_cond); - // tell the world we are done - pthread_mutex_unlock(&rli->run_lock); + pthread_mutex_unlock(&rli->run_lock); // tell the world we are done + my_thread_end(); pthread_exit(0); DBUG_RETURN(0); // Can't return anything here @@ -2615,7 +2640,7 @@ static int process_io_rotate(MASTER_INFO *mi, Rotate_log_event *rev) /* Safe copy as 'rev' has been "sanitized" in Rotate_log_event's ctor */ memcpy(mi->master_log_name, rev->new_log_ident, rev->ident_len+1); mi->master_log_pos= rev->pos; - DBUG_PRINT("info", ("master_log_pos: '%s' %d", + DBUG_PRINT("info", ("master_log_pos: '%s' %lu", mi->master_log_name, (ulong) mi->master_log_pos)); #ifndef DBUG_OFF /* @@ -2733,7 +2758,7 @@ static int queue_binlog_ver_1_event(MASTER_INFO *mi, const char *buf, int error = process_io_create_file(mi,(Create_file_log_event*)ev); delete ev; mi->master_log_pos += inc_pos; - DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos)); + DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos)); pthread_mutex_unlock(&mi->data_lock); my_free((char*)tmp_buf, MYF(0)); DBUG_RETURN(error); @@ -2760,7 +2785,7 @@ static int queue_binlog_ver_1_event(MASTER_INFO *mi, const char *buf, } delete ev; mi->master_log_pos+= inc_pos; - DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos)); + DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos)); pthread_mutex_unlock(&mi->data_lock); DBUG_RETURN(0); } @@ -2816,7 +2841,7 @@ static int queue_binlog_ver_3_event(MASTER_INFO *mi, const char *buf, delete ev; mi->master_log_pos+= inc_pos; err: - DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos)); + DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos)); pthread_mutex_unlock(&mi->data_lock); DBUG_RETURN(0); } @@ -2869,6 +2894,8 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) pthread_mutex_t *log_lock= rli->relay_log.get_log_lock(); DBUG_ENTER("queue_event"); + LINT_INIT(inc_pos); + if (mi->rli.relay_log.description_event_for_queue->binlog_version<4 && buf[EVENT_TYPE_OFFSET] != FORMAT_DESCRIPTION_EVENT /* a way to escape */) DBUG_RETURN(queue_old_event(mi,buf,event_len)); @@ -2989,7 +3016,8 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) rli->ign_master_log_pos_end= mi->master_log_pos; } rli->relay_log.signal_update(); // the slave SQL thread needs to re-check - DBUG_PRINT("info", ("master_log_pos: %d, event originating from the same server, ignored", (ulong) mi->master_log_pos)); + DBUG_PRINT("info", ("master_log_pos: %lu, event originating from the same server, ignored", + (ulong) mi->master_log_pos)); } else { @@ -2997,7 +3025,7 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) if (likely(!(rli->relay_log.appendv(buf,event_len,0)))) { mi->master_log_pos+= inc_pos; - DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos)); + DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos)); rli->relay_log.harvest_bytes_written(&rli->log_space_total); } else @@ -3009,7 +3037,7 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) err: pthread_mutex_unlock(&mi->data_lock); - DBUG_PRINT("info", ("error=%d", error)); + DBUG_PRINT("info", ("error: %d", error)); DBUG_RETURN(error); } @@ -3118,8 +3146,8 @@ static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi, { last_errno=mysql_errno(mysql); suppress_warnings= 0; - sql_print_error("Slave I/O thread: error %s to master \ -'%s@%s:%d': \ + sql_print_error("Slave I/O thread: error %s to master " + "'%s@%s:%d': \ Error: '%s' errno: %d retry-time: %d retries: %lu", (reconnect ? "reconnecting" : "connecting"), mi->user, mi->host, mi->port, @@ -3329,7 +3357,13 @@ static Log_event* next_event(RELAY_LOG_INFO* rli) hot_log=0; // Using old binary log } } - + /* + As there is no guarantee that the relay is open (for example, an I/O + error during a write by the slave I/O thread may have closed it), we + have to test it. + */ + if (!my_b_inited(cur_log)) + goto err; #ifndef DBUG_OFF { /* This is an assertion which sometimes fails, let's try to track it */ @@ -3658,6 +3692,70 @@ end: } +/** + Detects, based on master's version (as found in the relay log), if master + has a certain bug. + @param rli RELAY_LOG_INFO which tells the master's version + @param bug_id Number of the bug as found in bugs.mysql.com + @return TRUE if master has the bug, FALSE if it does not. +*/ +bool rpl_master_has_bug(RELAY_LOG_INFO *rli, uint bug_id) +{ + struct st_version_range_for_one_bug { + uint bug_id; + const uchar introduced_in[3]; // first version with bug + const uchar fixed_in[3]; // first version with fix + }; + static struct st_version_range_for_one_bug versions_for_all_bugs[]= + { + {24432, { 5, 0, 24 }, { 5, 0, 38 } }, + {24432, { 5, 1, 12 }, { 5, 1, 17 } } + }; + const uchar *master_ver= + rli->relay_log.description_event_for_exec->server_version_split; + + DBUG_ASSERT(sizeof(rli->relay_log.description_event_for_exec->server_version_split) == 3); + + for (uint i= 0; + i < sizeof(versions_for_all_bugs)/sizeof(*versions_for_all_bugs);i++) + { + const uchar *introduced_in= versions_for_all_bugs[i].introduced_in, + *fixed_in= versions_for_all_bugs[i].fixed_in; + if ((versions_for_all_bugs[i].bug_id == bug_id) && + (memcmp(introduced_in, master_ver, 3) <= 0) && + (memcmp(fixed_in, master_ver, 3) > 0)) + { + // a short message for SHOW SLAVE STATUS (message length constraints) + my_printf_error(ER_UNKNOWN_ERROR, "master may suffer from" + " http://bugs.mysql.com/bug.php?id=%u" + " so slave stops; check error log on slave" + " for more info", MYF(0), bug_id); + // a verbose message for the error log + slave_print_msg(ERROR_LEVEL, rli, ER_UNKNOWN_ERROR, + "According to the master's version ('%s')," + " it is probable that master suffers from this bug:" + " http://bugs.mysql.com/bug.php?id=%u" + " and thus replicating the current binary log event" + " may make the slave's data become different from the" + " master's data." + " To take no risk, slave refuses to replicate" + " this event and stops." + " We recommend that all updates be stopped on the" + " master and slave, that the data of both be" + " manually synchronized," + " that master's binary logs be deleted," + " that master be upgraded to a version at least" + " equal to '%d.%d.%d'. Then replication can be" + " restarted.", + rli->relay_log.description_event_for_exec->server_version, + bug_id, + fixed_in[0], fixed_in[1], fixed_in[2]); + return TRUE; + } + } + return FALSE; +} + #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION template class I_List_iterator<i_string>; template class I_List_iterator<i_string_pair>; diff --git a/sql/slave.h b/sql/slave.h index 1e5be3ab8bd..107b74c09dd 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -112,8 +111,6 @@ extern ulonglong relay_log_space_limit; #define MYSQL_SLAVE_RUN_NOT_CONNECT 1 #define MYSQL_SLAVE_RUN_CONNECT 2 -static Log_event* next_event(RELAY_LOG_INFO* rli); - #define RPL_LOG_NAME (rli->group_master_log_name[0] ? rli->group_master_log_name :\ "FIRST") #define IO_RPL_LOG_NAME (mi->master_log_name[0] ? mi->master_log_name :\ @@ -162,6 +159,7 @@ int fetch_master_table(THD* thd, const char* db_name, const char* table_name, bool show_master_info(THD* thd, MASTER_INFO* mi); bool show_binlog_info(THD* thd); +bool rpl_master_has_bug(RELAY_LOG_INFO *rli, uint bug_id); const char *print_slave_db_safe(const char *db); int check_expected_error(THD* thd, RELAY_LOG_INFO const *rli, int error_code); diff --git a/sql/sp.cc b/sql/sp.cc index 3411a18c17a..0c0bc8e8869 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -48,7 +47,7 @@ enum { MYSQL_PROC_FIELD_DB = 0, MYSQL_PROC_FIELD_NAME, - MYSQL_PROC_FIELD_TYPE, + MYSQL_PROC_MYSQL_TYPE, MYSQL_PROC_FIELD_SPECIFIC_NAME, MYSQL_PROC_FIELD_LANGUAGE, MYSQL_PROC_FIELD_ACCESS, @@ -495,18 +494,16 @@ db_create_routine(THD *thd, int type, sp_head *sp) int ret; TABLE *table; char definer[USER_HOST_BUFF_SIZE]; - char old_db_buf[NAME_LEN+1]; - LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) }; - bool dbchanged; DBUG_ENTER("db_create_routine"); DBUG_PRINT("enter", ("type: %d name: %.*s",type,sp->m_name.length, sp->m_name.str)); - if ((ret= sp_use_new_db(thd, sp->m_db, &old_db, 0, &dbchanged))) - { - ret= SP_NO_DB_ERROR; - goto done; - } + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); if (!(table= open_proc_table_for_update(thd))) ret= SP_OPEN_TABLE_FAILED; @@ -541,7 +538,7 @@ db_create_routine(THD *thd, int type, sp_head *sp) store(sp->m_db.str, sp->m_db.length, system_charset_info); table->field[MYSQL_PROC_FIELD_NAME]-> store(sp->m_name.str, sp->m_name.length, system_charset_info); - table->field[MYSQL_PROC_FIELD_TYPE]-> + table->field[MYSQL_PROC_MYSQL_TYPE]-> store((longlong)type, TRUE); table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]-> store(sp->m_name.str, sp->m_name.length, system_charset_info); @@ -631,8 +628,6 @@ db_create_routine(THD *thd, int type, sp_head *sp) done: close_thread_tables(thd); - if (dbchanged) - (void) mysql_change_db(thd, old_db.str, 1); DBUG_RETURN(ret); } @@ -646,6 +641,13 @@ db_drop_routine(THD *thd, int type, sp_name *name) DBUG_PRINT("enter", ("type: %d name: %.*s", type, name->m_name.length, name->m_name.str)); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) @@ -653,6 +655,17 @@ db_drop_routine(THD *thd, int type, sp_name *name) if (table->file->ha_delete_row(table->record[0])) ret= SP_DELETE_ROW_FAILED; } + + if (ret == SP_OK) + { + if (mysql_bin_log.is_open()) + { + thd->clear_error(); + thd->binlog_query(THD::MYSQL_QUERY_TYPE, + thd->query, thd->query_length, FALSE, FALSE); + } + } + close_thread_tables(thd); DBUG_RETURN(ret); } @@ -667,6 +680,13 @@ db_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) DBUG_PRINT("enter", ("type: %d name: %.*s", type, name->m_name.length, name->m_name.str)); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) @@ -687,6 +707,17 @@ db_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) if ((table->file->ha_update_row(table->record[1],table->record[0]))) ret= SP_WRITE_ROW_FAILED; } + + if (ret == SP_OK) + { + if (mysql_bin_log.is_open()) + { + thd->clear_error(); + thd->binlog_query(THD::MYSQL_QUERY_TYPE, + thd->query, thd->query_length, FALSE, FALSE); + } + } + close_thread_tables(thd); DBUG_RETURN(ret); } @@ -721,7 +752,7 @@ print_field_values(THD *thd, TABLE *table, { Protocol *protocol= thd->protocol; - if (table->field[MYSQL_PROC_FIELD_TYPE]->val_int() == type) + if (table->field[MYSQL_PROC_MYSQL_TYPE]->val_int() == type) { String db_string; String name_string; @@ -765,6 +796,7 @@ print_field_values(THD *thd, TABLE *table, return SP_INTERNAL_ERROR; } } + return SP_OK; } @@ -977,7 +1009,7 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp, DBUG_PRINT("info", ("found: 0x%lx", (ulong)sp)); if (sp->m_first_free_instance) { - DBUG_PRINT("info", ("first free: 0x%lx, level: %lu, flags %x", + DBUG_PRINT("info", ("first free: 0x%lx level: %lu flags %x", (ulong)sp->m_first_free_instance, sp->m_first_free_instance->m_recursion_level, sp->m_first_free_instance->m_flags)); @@ -1371,6 +1403,10 @@ static bool add_used_routine(LEX *lex, Query_arena *arena, const LEX_STRING *key, TABLE_LIST *belong_to_view) { + hash_init_opt(&lex->sroutines, system_charset_info, + Query_tables_list::START_SROUTINES_HASH_SIZE, + 0, 0, sp_sroutine_key, 0, 0); + if (!hash_search(&lex->sroutines, (byte *)key->str, key->length)) { Sroutine_hash_entry *rn= @@ -1822,9 +1858,7 @@ create_string(THD *thd, String *buf, SYNOPSIS sp_use_new_db() thd thread handle - new_db new database name (a string and its length) - old_db [IN] str points to a buffer where to store the old database, length contains the size of the buffer [OUT] if old db was not NULL, its name is copied @@ -1832,7 +1866,6 @@ create_string(THD *thd, String *buf, accordingly. Otherwise str[0] is set to '\0' and length is set to 0. The out parameter should be used only if the database name has been changed (see dbchangedp). - dbchangedp [OUT] is set to TRUE if the current database is changed, FALSE otherwise. A database is not changed if the old name is the same as the new one, both names are empty, @@ -3,8 +3,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index f5912caddaf..de4e1efd496 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sp_cache.h b/sql/sp_cache.h index 1021d17b9e2..9d34c9a2fb5 100644 --- a/sql/sp_cache.h +++ b/sql/sp_cache.h @@ -3,8 +3,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sp_head.cc b/sql/sp_head.cc index a061ae12dd1..1e0986f6e82 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -37,6 +36,7 @@ Item_result sp_map_result_type(enum enum_field_types type) { switch (type) { + case MYSQL_TYPE_BIT: case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_LONG: @@ -59,6 +59,7 @@ Item::Type sp_map_item_type(enum enum_field_types type) { switch (type) { + case MYSQL_TYPE_BIT: case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_LONG: @@ -93,10 +94,8 @@ sp_map_item_type(enum enum_field_types type) */ static String * -sp_get_item_value(Item *item, String *str) +sp_get_item_value(THD *thd, Item *item, String *str) { - Item_result result_type= item->result_type(); - switch (item->result_type()) { case REAL_RESULT: case INT_RESULT: @@ -113,15 +112,16 @@ sp_get_item_value(Item *item, String *str) { char buf_holder[STRING_BUFFER_USUAL_SIZE]; String buf(buf_holder, sizeof(buf_holder), result->charset()); + CHARSET_INFO *cs= thd->variables.character_set_client; /* We must reset length of the buffer, because of String specificity. */ buf.length(0); buf.append('_'); buf.append(result->charset()->csname); - if (result->charset()->escape_with_backslash_is_dangerous) + if (cs->escape_with_backslash_is_dangerous) buf.append(' '); - append_query_string(result->charset(), result, &buf); + append_query_string(cs, result, &buf); str->copy(buf); return str; @@ -456,10 +456,14 @@ sp_head::operator delete(void *ptr, size_t size) sp_head::sp_head() :Query_arena(&main_mem_root, INITIALIZED_FOR_SP), m_flags(0), m_recursion_level(0), m_next_cached_sp(0), - m_first_instance(this), m_first_free_instance(this), m_last_cached_sp(this), m_cont_level(0) { const LEX_STRING str_reset= { NULL, 0 }; + + m_first_instance= this; + m_first_free_instance= this; + m_last_cached_sp= this; + m_return_field_def.charset = NULL; /* FIXME: the only use case when name is NULL is events, and it should @@ -625,27 +629,6 @@ sp_head::create(THD *thd) DBUG_PRINT("info", ("type: %d name: %s params: %s body: %s", m_type, m_name.str, m_params.str, m_body.str)); -#ifndef DBUG_OFF - optimize(); - { - String s; - sp_instr *i; - uint ip= 0; - while ((i = get_instr(ip))) - { - char buf[8]; - - sprintf(buf, "%4u: ", ip); - s.append(buf); - i->print(&s); - s.append('\n'); - ip+= 1; - } - s.append('\0'); - DBUG_PRINT("info", ("Code %s\n%s", m_qname.str, s.ptr())); - } -#endif - if (m_type == TYPE_ENUM_FUNCTION) ret= sp_create_function(thd, this); else @@ -899,8 +882,8 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) break; val= (*splocal)->this_item(); - DBUG_PRINT("info", ("print %p", val)); - str_value= sp_get_item_value(val, &str_value_holder); + DBUG_PRINT("info", ("print 0x%lx", (long) val)); + str_value= sp_get_item_value(thd, val, &str_value_holder); if (str_value) res|= qbuf.append(*str_value); else @@ -1029,8 +1012,7 @@ sp_head::execute(THD *thd) save_sql_mode= thd->variables.sql_mode; thd->variables.sql_mode= m_sql_mode; save_abort_on_warning= thd->abort_on_warning; - thd->abort_on_warning= - (m_sql_mode & (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)); + thd->abort_on_warning= 0; /* It is also more efficient to save/restore current thd->lex once when @@ -1143,6 +1125,7 @@ sp_head::execute(THD *thd) ctx->clear_handler(); ctx->enter_handler(hip); thd->clear_error(); + thd->is_fatal_error= 0; thd->killed= THD::NOT_KILLED; continue; } @@ -1170,8 +1153,9 @@ sp_head::execute(THD *thd) state= EXECUTED; done: - DBUG_PRINT("info", ("err_status: %d killed: %d query_error: %d", - err_status, thd->killed, thd->query_error)); + DBUG_PRINT("info", ("err_status: %d killed: %d query_error: %d report_error: %d", + err_status, thd->killed, thd->query_error, + thd->net.report_error)); if (thd->killed) err_status= TRUE; @@ -1385,7 +1369,6 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, MEM_ROOT call_mem_root; Query_arena call_arena(&call_mem_root, Query_arena::INITIALIZED_FOR_SP); Query_arena backup_arena; - DBUG_ENTER("sp_head::execute_function"); DBUG_PRINT("info", ("function %s", m_name.str)); @@ -1466,6 +1449,8 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, { binlog_buf.length(0); binlog_buf.append(STRING_WITH_LEN("SELECT ")); + append_identifier(thd, &binlog_buf, m_db.str, m_db.length); + binlog_buf.append('.'); append_identifier(thd, &binlog_buf, m_name.str, m_name.length); binlog_buf.append('('); for (arg_no= 0; arg_no < argcount; arg_no++) @@ -1476,7 +1461,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, if (arg_no) binlog_buf.append(','); - str_value= sp_get_item_value(nctx->get_item(arg_no), + str_value= sp_get_item_value(thd, nctx->get_item(arg_no), &str_value_holder); if (str_value) @@ -1499,8 +1484,24 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, if (need_binlog_call) { + query_id_t q; reset_dynamic(&thd->user_var_events); - mysql_bin_log.start_union_events(thd); + /* + In case of artificially constructed events for function calls + we have separate union for each such event and hence can't use + query_id of real calling statement as the start of all these + unions (this will break logic of replication of user-defined + variables). So we use artifical value which is guaranteed to + be greater than all query_id's of all statements belonging + to previous events/unions. + Possible alternative to this is logging of all function invocations + as one select and not resetting THD::user_var_events before + each invocation. + */ + VOID(pthread_mutex_lock(&LOCK_thread_count)); + q= global_query_id; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + mysql_bin_log.start_union_events(thd, q + 1); binlog_save_options= thd->options; thd->options&= ~OPTION_BIN_LOG; } @@ -1674,7 +1675,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) Item_null *null_item= new Item_null(); if (!null_item || - nctx->set_variable(thd, i, (struct Item **)&null_item)) + nctx->set_variable(thd, i, (Item **)&null_item)) { err_status= TRUE; break; @@ -1791,7 +1792,7 @@ sp_head::reset_lex(THD *thd) DBUG_ENTER("sp_head::reset_lex"); LEX *sublex; LEX *oldlex= thd->lex; - my_lex_states state= oldlex->next_state; // Keep original next_state + my_lex_states org_next_state= oldlex->next_state; (void)m_lex.push_front(oldlex); thd->lex= sublex= new st_lex; @@ -1800,10 +1801,10 @@ sp_head::reset_lex(THD *thd) lex_start(thd, oldlex->buf, (ulong) (oldlex->end_of_query - oldlex->ptr)); /* - * next_state is normally the same (0), but it happens that we swap lex in - * "mid-sentence", so we must restore it. + next_state is normally the same (0), but it happens that we swap lex in + "mid-sentence", so we must restore it. */ - sublex->next_state= state; + sublex->next_state= org_next_state; /* We must reset ptr and end_of_query again */ sublex->ptr= oldlex->ptr; sublex->end_of_query= oldlex->end_of_query; @@ -1848,7 +1849,6 @@ sp_head::restore_lex(THD *thd) oldlex->next_state= sublex->next_state; oldlex->trg_table_fields.push_back(&sublex->trg_table_fields); -#ifdef HAVE_ROW_BASED_REPLICATION /* If this substatement needs row-based, the entire routine does too (we cannot switch from statement-based to row-based only for this @@ -1856,7 +1856,6 @@ sp_head::restore_lex(THD *thd) */ if (sublex->binlog_row_based_if_mixed) m_flags|= BINLOG_ROW_BASED_IF_MIXED; -#endif /* Add routines which are used by statement to respective set for @@ -2225,7 +2224,7 @@ sp_head::show_create_function(THD *thd) This is the main mark and move loop; it relies on the following methods in sp_instr and its subclasses: - opt_mark() Mark instruction as reachable (will recurse for jumps) + opt_mark() Mark instruction as reachable opt_shortcut_jump() Shortcut jumps to the final destination; used by opt_mark(). opt_move() Update moved instruction @@ -2238,7 +2237,7 @@ void sp_head::optimize() sp_instr *i; uint src, dst; - opt_mark(0); + opt_mark(); bp.empty(); src= dst= 0; @@ -2272,13 +2271,50 @@ void sp_head::optimize() bp.empty(); } +void sp_head::add_mark_lead(uint ip, List<sp_instr> *leads) +{ + sp_instr *i= get_instr(ip); + + if (i && ! i->marked) + leads->push_front(i); +} + void -sp_head::opt_mark(uint ip) +sp_head::opt_mark() { + uint ip; sp_instr *i; + List<sp_instr> leads; + + /* + Forward flow analysis algorithm in the instruction graph: + - first, add the entry point in the graph (the first instruction) to the + 'leads' list of paths to explore. + - while there are still leads to explore: + - pick one lead, and follow the path forward. Mark instruction reached. + Stop only if the end of the routine is reached, or the path converge + to code already explored (marked). + - while following a path, collect in the 'leads' list any fork to + another path (caused by conditional jumps instructions), so that these + paths can be explored as well. + */ - while ((i= get_instr(ip)) && !i->marked) - ip= i->opt_mark(this); + /* Add the entry point */ + i= get_instr(0); + leads.push_front(i); + + /* For each path of code ... */ + while (leads.elements != 0) + { + i= leads.pop(); + + /* Mark the entire path, collecting new leads. */ + while (i && ! i->marked) + { + ip= i->opt_mark(this, & leads); + i= get_instr(ip); + } + } } @@ -2375,6 +2411,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables, sp_instr* instr) { int res= 0; + DBUG_ENTER("reset_lex_and_exec_core"); DBUG_ASSERT(!thd->derived_tables); DBUG_ASSERT(thd->change_list.is_empty()); @@ -2407,19 +2444,17 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, m_lex->mark_as_requiring_prelocking(lex_query_tables_own_last); } } - + reinit_stmt_before_use(thd, m_lex); - /* - If requested check whenever we have access to tables in LEX's table list - and open and lock them before executing instructtions core function. - */ - if (open_tables && - (check_table_access(thd, SELECT_ACL, m_lex->query_tables, 0) || - open_and_lock_tables(thd, m_lex->query_tables))) - res= -1; + + if (open_tables) + res= instr->exec_open_and_lock_tables(thd, m_lex->query_tables, nextp); if (!res) + { res= instr->exec_core(thd, nextp); + DBUG_PRINT("info",("exec_core returned: %d", res)); + } m_lex->unit.cleanup(); @@ -2457,7 +2492,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, cleanup_items() is called in sp_head::execute() */ - return res || thd->net.report_error; + DBUG_RETURN(res || thd->net.report_error); } @@ -2465,6 +2500,33 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, sp_instr class functions */ +int sp_instr::exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables, + uint *nextp) +{ + int result; + + /* + Check whenever we have access to tables for this statement + and open and lock them before executing instructions core function. + */ + if (check_table_access(thd, SELECT_ACL, tables, 0) + || open_and_lock_tables(thd, tables)) + { + get_cont_dest(nextp); + result= -1; + } + else + result= 0; + + return result; +} + +void sp_instr::get_cont_dest(uint *nextp) +{ + *nextp= m_ip+1; +} + + int sp_instr::exec_core(THD *thd, uint *nextp) { DBUG_ASSERT(0); @@ -2650,6 +2712,15 @@ sp_instr_set_trigger_field::print(String *str) value->print(str); } +/* + sp_instr_opt_meta +*/ + +void sp_instr_opt_meta::get_cont_dest(uint *nextp) +{ + *nextp= m_cont_dest; +} + /* sp_instr_jump class functions @@ -2676,7 +2747,7 @@ sp_instr_jump::print(String *str) } uint -sp_instr_jump::opt_mark(sp_head *sp) +sp_instr_jump::opt_mark(sp_head *sp, List<sp_instr> *leads) { m_dest= opt_shortcut_jump(sp, this); if (m_dest != m_ip+1) /* Jumping to following instruction? */ @@ -2770,7 +2841,7 @@ sp_instr_jump_if_not::print(String *str) uint -sp_instr_jump_if_not::opt_mark(sp_head *sp) +sp_instr_jump_if_not::opt_mark(sp_head *sp, List<sp_instr> *leads) { sp_instr *i; @@ -2780,13 +2851,13 @@ sp_instr_jump_if_not::opt_mark(sp_head *sp) m_dest= i->opt_shortcut_jump(sp, this); m_optdest= sp->get_instr(m_dest); } - sp->opt_mark(m_dest); + sp->add_mark_lead(m_dest, leads); if ((i= sp->get_instr(m_cont_dest))) { m_cont_dest= i->opt_shortcut_jump(sp, this); m_cont_optdest= sp->get_instr(m_cont_dest); } - sp->opt_mark(m_cont_dest); + sp->add_mark_lead(m_cont_dest, leads); return m_ip+1; } @@ -2848,7 +2919,7 @@ void sp_instr_freturn::print(String *str) { /* freturn type expr... */ - if (str->reserve(UINT_MAX+8+32)) // Add some for the expr. too + if (str->reserve(1024+8+32)) // Add some for the expr. too return; str->qs_append(STRING_WITH_LEN("freturn ")); str->qs_append((uint)m_type); @@ -2907,7 +2978,7 @@ sp_instr_hpush_jump::print(String *str) uint -sp_instr_hpush_jump::opt_mark(sp_head *sp) +sp_instr_hpush_jump::opt_mark(sp_head *sp, List<sp_instr> *leads) { sp_instr *i; @@ -2917,7 +2988,7 @@ sp_instr_hpush_jump::opt_mark(sp_head *sp) m_dest= i->opt_shortcut_jump(sp, this); m_optdest= sp->get_instr(m_dest); } - sp->opt_mark(m_dest); + sp->add_mark_lead(m_dest, leads); return m_ip+1; } @@ -2982,15 +3053,13 @@ sp_instr_hreturn::print(String *str) uint -sp_instr_hreturn::opt_mark(sp_head *sp) +sp_instr_hreturn::opt_mark(sp_head *sp, List<sp_instr> *leads) { if (m_dest) - return sp_instr_jump::opt_mark(sp); - else - { - marked= 1; - return UINT_MAX; - } + return sp_instr_jump::opt_mark(sp, leads); + + marked= 1; + return UINT_MAX; } @@ -3333,7 +3402,7 @@ sp_instr_set_case_expr::print(String *str) } uint -sp_instr_set_case_expr::opt_mark(sp_head *sp) +sp_instr_set_case_expr::opt_mark(sp_head *sp, List<sp_instr> *leads) { sp_instr *i; @@ -3343,7 +3412,7 @@ sp_instr_set_case_expr::opt_mark(sp_head *sp) m_cont_dest= i->opt_shortcut_jump(sp, this); m_cont_optdest= sp->get_instr(m_cont_dest); } - sp->opt_mark(m_cont_dest); + sp->add_mark_lead(m_cont_dest, leads); return m_ip+1; } diff --git a/sql/sp_head.h b/sql/sp_head.h index 41ed5256840..46ad3dd96d8 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -3,8 +3,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -304,8 +303,19 @@ public: void restore_thd_mem_root(THD *thd); + /** + Optimize the code. + */ void optimize(); - void opt_mark(uint ip); + + /** + Helper used during flow analysis during code optimization. + See the implementation of <code>opt_mark()</code>. + @param ip the instruction to add to the leads list + @param leads the list of remaining paths to explore in the graph that + represents the code, during flow analysis. + */ + void add_mark_lead(uint ip, List<sp_instr> *leads); void recursion_level_error(THD *thd); @@ -362,7 +372,6 @@ public: */ void propagate_attributes(LEX *lex) { -#ifdef HAVE_ROW_BASED_REPLICATION /* If this routine needs row-based binary logging, the entire top statement too (we cannot switch from statement-based to row-based only for this @@ -371,7 +380,6 @@ public: */ if (m_flags & BINLOG_ROW_BASED_IF_MIXED) lex->binlog_row_based_if_mixed= TRUE; -#endif } @@ -414,6 +422,12 @@ private: bool execute(THD *thd); + /** + Perform a forward flow analysis in the generated code. + Mark reachable instructions, for the optimizer. + */ + void opt_mark(); + /* Merge the list of tables used by query into the multi-set of tables used by routine. @@ -463,6 +477,28 @@ public: virtual int execute(THD *thd, uint *nextp) = 0; + /** + Execute <code>open_and_lock_tables()</code> for this statement. + Open and lock the tables used by this statement, as a pre-requisite + to execute the core logic of this instruction with + <code>exec_core()</code>. + If this statement fails, the next instruction to execute is also returned. + This is useful when a user defined SQL continue handler needs to be + executed. + @param thd the current thread + @param tables the list of tables to open and lock + @param nextp the continuation instruction, returned to the caller if this + method fails. + @return zero on success, non zero on failure. + */ + int exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables, uint *nextp); + + /** + Get the continuation destination of this instruction. + @param nextp the continuation destination (output) + */ + virtual void get_cont_dest(uint *nextp); + /* Execute core function of instruction after all preparations (e.g. setting of proper LEX, saving part of the thread context have been @@ -481,10 +517,10 @@ public: /* Mark this instruction as reachable during optimization and return the - index to the next instruction. Jump instruction will mark their - destination too recursively. + index to the next instruction. Jump instruction will add their + destination to the leads list. */ - virtual uint opt_mark(sp_head *sp) + virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads) { marked= 1; return m_ip+1; @@ -639,9 +675,9 @@ class sp_instr_set : public sp_instr public: sp_instr_set(uint ip, sp_pcontext *ctx, - uint offset, Item *val, enum enum_field_types type, + uint offset, Item *val, enum enum_field_types type_arg, LEX *lex, bool lex_resp) - : sp_instr(ip, ctx), m_offset(offset), m_value(val), m_type(type), + : sp_instr(ip, ctx), m_offset(offset), m_value(val), m_type(type_arg), m_lex_keeper(lex, lex_resp) {} @@ -727,6 +763,8 @@ public: virtual void set_destination(uint old_dest, uint new_dest) = 0; + virtual void get_cont_dest(uint *nextp); + protected: sp_instr *m_optdest; // Used during optimization @@ -756,7 +794,7 @@ public: virtual void print(String *str); - virtual uint opt_mark(sp_head *sp); + virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads); virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start); @@ -806,7 +844,7 @@ public: virtual void print(String *str); - virtual uint opt_mark(sp_head *sp); + virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads); /* Override sp_instr_jump's shortcut; we stop here */ virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start) @@ -839,8 +877,9 @@ class sp_instr_freturn : public sp_instr public: sp_instr_freturn(uint ip, sp_pcontext *ctx, - Item *val, enum enum_field_types type, LEX *lex) - : sp_instr(ip, ctx), m_value(val), m_type(type), m_lex_keeper(lex, TRUE) + Item *val, enum enum_field_types type_arg, LEX *lex) + : sp_instr(ip, ctx), m_value(val), m_type(type_arg), + m_lex_keeper(lex, TRUE) {} virtual ~sp_instr_freturn() @@ -852,7 +891,7 @@ public: virtual void print(String *str); - virtual uint opt_mark(sp_head *sp) + virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads) { marked= 1; return UINT_MAX; @@ -889,7 +928,7 @@ public: virtual void print(String *str); - virtual uint opt_mark(sp_head *sp); + virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads); /* Override sp_instr_jump's shortcut; we stop here. */ virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start) @@ -954,7 +993,7 @@ public: virtual void print(String *str); - virtual uint opt_mark(sp_head *sp); + virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads); private: @@ -1124,7 +1163,7 @@ public: virtual void print(String *str); - virtual uint opt_mark(sp_head *sp) + virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads) { marked= 1; return UINT_MAX; @@ -1157,7 +1196,7 @@ public: virtual void print(String *str); - virtual uint opt_mark(sp_head *sp); + virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads); virtual void opt_move(uint dst, List<sp_instr> *ibp); diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index b0b65d5313b..6229cf14604 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index 2ee77696efb..b2cdd5e689c 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -3,8 +3,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 67ee5459bb4..e49c4eb1240 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 5e03aa60d23..fbf479f52aa 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -3,8 +3,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/spatial.cc b/sql/spatial.cc index 02f1525f2ad..e0680ed182c 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -24,13 +23,13 @@ String Geometry::bad_geometry_data("Bad object", &my_charset_bin); -Geometry::Class_info *Geometry::ci_collection[Geometry::wkb_end+1]= +Geometry::Class_info *Geometry::ci_collection[Geometry::wkb_last+1]= { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static Geometry::Class_info **ci_collection_end= - Geometry::ci_collection+Geometry::wkb_end + 1; + Geometry::ci_collection+Geometry::wkb_last + 1; Geometry::Class_info::Class_info(const char *name, int type_id, void(*create_func)(void *)): @@ -217,23 +216,24 @@ static uint32 wkb_get_uint(const char *ptr, Geometry::wkbByteOrder bo) } -int Geometry::create_from_wkb(Geometry_buffer *buffer, - const char *wkb, uint32 len, String *res) +Geometry *Geometry::create_from_wkb(Geometry_buffer *buffer, + const char *wkb, uint32 len, String *res) { uint32 geom_type; Geometry *geom; if (len < WKB_HEADER_SIZE) - return 1; + return NULL; geom_type= wkb_get_uint(wkb+1, (wkbByteOrder)wkb[0]); if (!(geom= create_by_typeid(buffer, (int) geom_type)) || res->reserve(WKB_HEADER_SIZE, 512)) - return 1; + return NULL; res->q_append((char) wkb_ndr); res->q_append(geom_type); - return geom->init_from_wkb(wkb+WKB_HEADER_SIZE, len - WKB_HEADER_SIZE, - (wkbByteOrder) wkb[0], res); + + return geom->init_from_wkb(wkb + WKB_HEADER_SIZE, len - WKB_HEADER_SIZE, + (wkbByteOrder) wkb[0], res) ? geom : NULL; } @@ -553,7 +553,7 @@ bool Gis_line_string::get_mbr(MBR *mbr, const char **end) const } -int Gis_line_string::length(double *len) const +int Gis_line_string::geom_length(double *len) const { uint32 n_points; double prev_x, prev_y; @@ -929,6 +929,8 @@ int Gis_polygon::centroid_xy(double *x, double *y) const n_linear_rings= uint4korr(data); data+= 4; + DBUG_ASSERT(n_linear_rings > 0); + while (n_linear_rings--) { uint32 n_points, org_n_points; @@ -948,14 +950,14 @@ int Gis_polygon::centroid_xy(double *x, double *y) const while (--n_points) // One point is already read { - double x, y; - get_point(&x, &y, data); + double tmp_x, tmp_y; + get_point(&tmp_x, &tmp_y, data); data+= (SIZEOF_STORED_DOUBLE*2); - cur_area+= (prev_x + x) * (prev_y - y); - cur_cx+= x; - cur_cy+= y; - prev_x= x; - prev_y= y; + cur_area+= (prev_x + tmp_x) * (prev_y - tmp_y); + cur_cx+= tmp_x; + cur_cy+= tmp_y; + prev_x= tmp_x; + prev_y= tmp_y; } cur_area= fabs(cur_area) / 2; cur_cx= cur_cx / (org_n_points - 1); @@ -1299,7 +1301,7 @@ int Gis_multi_line_string::geometry_n(uint32 num, String *result) const } -int Gis_multi_line_string::length(double *len) const +int Gis_multi_line_string::geom_length(double *len) const { uint32 n_line_strings; const char *data= m_data; @@ -1316,7 +1318,7 @@ int Gis_multi_line_string::length(double *len) const Gis_line_string ls; data+= WKB_HEADER_SIZE; ls.set_data_ptr(data, (uint32) (m_data_end - data)); - if (ls.length(&ls_len)) + if (ls.geom_length(&ls_len)) return 1; *len+= ls_len; /* diff --git a/sql/spatial.h b/sql/spatial.h index 36949ff5014..0c0452b5abc 100644 --- a/sql/spatial.h +++ b/sql/spatial.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2002-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -189,7 +188,7 @@ public: wkb_multilinestring= 5, wkb_multipolygon= 6, wkb_geometrycollection= 7, - wkb_end=7 + wkb_last=7 }; enum wkbByteOrder { @@ -218,7 +217,7 @@ public: virtual bool dimension(uint32 *dim, const char **end) const=0; virtual int get_x(double *x) const { return -1; } virtual int get_y(double *y) const { return -1; } - virtual int length(double *len) const { return -1; } + virtual int geom_length(double *len) const { return -1; } virtual int area(double *ar, const char **end) const { return -1;} virtual int is_closed(int *closed) const { return -1; } virtual int num_interior_ring(uint32 *n_int_rings) const { return -1; } @@ -247,8 +246,8 @@ public: static Geometry *create_from_wkt(Geometry_buffer *buffer, Gis_read_stream *trs, String *wkt, bool init_stream=1); - static int create_from_wkb(Geometry_buffer *buffer, - const char *wkb, uint32 len, String *res); + static Geometry *create_from_wkb(Geometry_buffer *buffer, const char *wkb, + uint32 len, String *res); int as_wkt(String *wkt, const char **end) { uint32 len= get_class_info()->m_name.length; @@ -274,12 +273,12 @@ public: } bool envelope(String *result) const; - static Class_info *ci_collection[wkb_end+1]; + static Class_info *ci_collection[wkb_last+1]; protected: static Class_info *find_class(int type_id) { - return ((type_id < wkb_point) || (type_id > wkb_end)) ? + return ((type_id < wkb_point) || (type_id > wkb_last)) ? NULL : ci_collection[type_id]; } static Class_info *find_class(const char *name, uint32 len); @@ -360,7 +359,7 @@ public: uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); bool get_data_as_wkt(String *txt, const char **end) const; bool get_mbr(MBR *mbr, const char **end) const; - int length(double *len) const; + int geom_length(double *len) const; int is_closed(int *closed) const; int num_points(uint32 *n_points) const; int start_point(String *point) const; @@ -442,7 +441,7 @@ public: bool get_mbr(MBR *mbr, const char **end) const; int num_geometries(uint32 *num) const; int geometry_n(uint32 num, String *result) const; - int length(double *len) const; + int geom_length(double *len) const; int is_closed(int *closed) const; bool dimension(uint32 *dim, const char **end) const { diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index c55fc744cd0..ee7127fcd8d 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -761,7 +760,7 @@ static ulong get_access(TABLE *form, uint fieldnr, uint *next_field) Field **pos; for (pos=form->field+fieldnr, bit=1; - *pos && (*pos)->real_type() == FIELD_TYPE_ENUM && + *pos && (*pos)->real_type() == MYSQL_TYPE_ENUM && ((Field_enum*) (*pos))->typelib->count == 2 ; pos++, fieldnr++, bit<<=1) { @@ -1630,7 +1629,7 @@ bool change_password(THD *thd, const char *host, const char *user, { query_length= my_sprintf(buff, - (buff,"SET PASSWORD FOR \"%-.120s\"@\"%-.120s\"=\"%-.120s\"", + (buff,"SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'", acl_user->user ? acl_user->user : "", acl_user->host.hostname ? acl_user->host.hostname : "", new_password)); @@ -1969,7 +1968,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, ulong priv; uint next_field; for (tmp_field= table->field+3, priv = SELECT_ACL; - *tmp_field && (*tmp_field)->real_type() == FIELD_TYPE_ENUM && + *tmp_field && (*tmp_field)->real_type() == MYSQL_TYPE_ENUM && ((Field_enum*) (*tmp_field))->typelib->count == 2 ; tmp_field++, priv <<= 1) { @@ -3002,6 +3001,13 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list, tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE; tables[0].db=tables[1].db=tables[2].db=(char*) "mysql"; + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + #ifdef HAVE_REPLICATION /* GRANT and REVOKE are applied the slave in/exclusion rules as they are @@ -3140,9 +3146,22 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list, grant_option=TRUE; thd->mem_root= old_root; pthread_mutex_unlock(&acl_cache->lock); + + if (!result) /* success */ + { + if (mysql_bin_log.is_open()) + { + thd->clear_error(); + thd->binlog_query(THD::MYSQL_QUERY_TYPE, + thd->query, thd->query_length, FALSE, FALSE); + } + } + rw_unlock(&LOCK_grant); - if (!result) + + if (!result) /* success */ send_ok(thd); + /* Tables are automatically closed */ DBUG_RETURN(result); } @@ -3206,6 +3225,13 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + #ifdef HAVE_REPLICATION /* GRANT and REVOKE are applied the slave in/exclusion rules as they are @@ -3294,9 +3320,21 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, grant_option=TRUE; thd->mem_root= old_root; pthread_mutex_unlock(&acl_cache->lock); + if (!result && !no_error) + { + if (mysql_bin_log.is_open()) + { + thd->clear_error(); + thd->binlog_query(THD::MYSQL_QUERY_TYPE, + thd->query, thd->query_length, FALSE, FALSE); + } + } + rw_unlock(&LOCK_grant); + if (!result && !no_error) send_ok(thd); + /* Tables are automatically closed */ DBUG_RETURN(result); } @@ -3333,6 +3371,13 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + #ifdef HAVE_REPLICATION /* GRANT and REVOKE are applied the slave in/exclusion rules as they are @@ -3394,11 +3439,23 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, } } VOID(pthread_mutex_unlock(&acl_cache->lock)); + + if (!result) + { + if (mysql_bin_log.is_open()) + { + thd->clear_error(); + thd->binlog_query(THD::MYSQL_QUERY_TYPE, + thd->query, thd->query_length, FALSE, FALSE); + } + } + rw_unlock(&LOCK_grant); close_thread_tables(thd); if (!result) send_ok(thd); + DBUG_RETURN(result); } @@ -3834,7 +3891,7 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant, GRANT_COLUMN *grant_column; ulong want_access= grant->want_privilege & ~grant->privilege; DBUG_ENTER("check_grant_column"); - DBUG_PRINT("enter", ("table: %s want_access: %u", table_name, want_access)); + DBUG_PRINT("enter", ("table: %s want_access: %lu", table_name, want_access)); if (!want_access) DBUG_RETURN(0); // Already checked @@ -5133,6 +5190,8 @@ static int handle_grant_struct(uint struct_no, bool drop, user= grant_name->user; host= grant_name->host.hostname; break; + default: + assert(0); } if (! user) user= ""; @@ -5363,6 +5422,13 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list) TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_create_user"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* CREATE USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5398,6 +5464,13 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list) } VOID(pthread_mutex_unlock(&acl_cache->lock)); + + if (mysql_bin_log.is_open()) + { + thd->binlog_query(THD::MYSQL_QUERY_TYPE, + thd->query, thd->query_length, FALSE, FALSE); + } + rw_unlock(&LOCK_grant); close_thread_tables(thd); if (result) @@ -5428,6 +5501,13 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list) TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_drop_user"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* DROP USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5454,6 +5534,13 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list) rebuild_check_host(); VOID(pthread_mutex_unlock(&acl_cache->lock)); + + if (mysql_bin_log.is_open()) + { + thd->binlog_query(THD::MYSQL_QUERY_TYPE, + thd->query, thd->query_length, FALSE, FALSE); + } + rw_unlock(&LOCK_grant); close_thread_tables(thd); if (result) @@ -5485,6 +5572,13 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list) TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_rename_user"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* RENAME USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5523,6 +5617,13 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list) rebuild_check_host(); VOID(pthread_mutex_unlock(&acl_cache->lock)); + + if (mysql_bin_log.is_open()) + { + thd->binlog_query(THD::MYSQL_QUERY_TYPE, + thd->query, thd->query_length, FALSE, FALSE); + } + rw_unlock(&LOCK_grant); close_thread_tables(thd); if (result) @@ -5553,6 +5654,13 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_revoke_all"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5697,6 +5805,13 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) } VOID(pthread_mutex_unlock(&acl_cache->lock)); + + if (mysql_bin_log.is_open()) + { + thd->binlog_query(THD::MYSQL_QUERY_TYPE, + thd->query, thd->query_length, FALSE, FALSE); + } + rw_unlock(&LOCK_grant); close_thread_tables(thd); @@ -5736,6 +5851,13 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, rw_wrlock(&LOCK_grant); VOID(pthread_mutex_lock(&acl_cache->lock)); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* Remove procedure access */ do { diff --git a/sql/sql_acl.h b/sql/sql_acl.h index e1153522ed5..86d2cabc703 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index 264e3e2b988..e7decf4a8fc 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -87,6 +86,11 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result, else if (param->next) { // first parameter + if (!(*param->item)->fixed && (*param->item)->fix_fields(thd, param->item)) + { + DBUG_PRINT("info", ("fix_fields() for the first parameter failed")); + goto err; + } if ((*param->item)->type() != Item::INT_ITEM || (*param->item)->val_real() < 0) { @@ -101,6 +105,11 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result, goto err; } // second parameter + if (!(*param->item)->fixed && (*param->item)->fix_fields(thd, param->item)) + { + DBUG_PRINT("info", ("fix_fields() for the second parameter failed")); + goto err; + } if ((*param->item)->type() != Item::INT_ITEM || (*param->item)->val_real() < 0) { @@ -140,7 +149,7 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result, case INT_RESULT: // Check if fieldtype is ulonglong if (item->type() == Item::FIELD_ITEM && - ((Item_field*) item)->field->type() == FIELD_TYPE_LONGLONG && + ((Item_field*) item)->field->type() == MYSQL_TYPE_LONGLONG && ((Field_longlong*) ((Item_field*) item)->field)->unsigned_flag) new_field= new field_ulonglong(item, pc); else @@ -755,26 +764,26 @@ bool analyse::end_of_records() { switch (((Item_field*) (*f)->item)->field->real_type()) { - case FIELD_TYPE_TIMESTAMP: + case MYSQL_TYPE_TIMESTAMP: ans.append(STRING_WITH_LEN("TIMESTAMP")); break; - case FIELD_TYPE_DATETIME: + case MYSQL_TYPE_DATETIME: ans.append(STRING_WITH_LEN("DATETIME")); break; - case FIELD_TYPE_DATE: - case FIELD_TYPE_NEWDATE: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: ans.append(STRING_WITH_LEN("DATE")); break; - case FIELD_TYPE_SET: + case MYSQL_TYPE_SET: ans.append(STRING_WITH_LEN("SET")); break; - case FIELD_TYPE_YEAR: + case MYSQL_TYPE_YEAR: ans.append(STRING_WITH_LEN("YEAR")); break; - case FIELD_TYPE_TIME: + case MYSQL_TYPE_TIME: ans.append(STRING_WITH_LEN("TIME")); break; - case FIELD_TYPE_DECIMAL: + case MYSQL_TYPE_DECIMAL: ans.append(STRING_WITH_LEN("DECIMAL")); // if item is FIELD_ITEM, it _must_be_ Field_num in this case if (((Field_num*) ((Item_field*) (*f)->item)->field)->zerofill) diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h index 0e7bd1b722a..ac671b85e1e 100644 --- a/sql/sql_analyse.h +++ b/sql/sql_analyse.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2003, 2005 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_array.h b/sql/sql_array.h index c68caf74b25..e2e12bee241 100644 --- a/sql/sql_array.h +++ b/sql/sql_array.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 3e7bcf7798a..e764c498059 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -29,6 +28,59 @@ #include <io.h> #endif +/** + This internal handler is used to trap internally + errors that can occur when executing open table + during the prelocking phase. +*/ +class Prelock_error_handler : public Internal_error_handler +{ +public: + Prelock_error_handler() + : m_handled_errors(0), m_unhandled_errors(0) + {} + + virtual ~Prelock_error_handler() {} + + virtual bool handle_error(uint sql_errno, + MYSQL_ERROR::enum_warning_level level, + THD *thd); + + bool safely_trapped_errors(); + +private: + int m_handled_errors; + int m_unhandled_errors; +}; + + +bool +Prelock_error_handler::handle_error(uint sql_errno, + MYSQL_ERROR::enum_warning_level /* level */, + THD * /* thd */) +{ + if (sql_errno == ER_NO_SUCH_TABLE) + { + m_handled_errors++; + return TRUE; // 'TRUE', as per coding style + } + + m_unhandled_errors++; + return FALSE; // 'FALSE', as per coding style +} + + +bool Prelock_error_handler::safely_trapped_errors() +{ + /* + If m_unhandled_errors != 0, something else, unanticipated, happened, + so the error is not trapped but returned to the caller. + Multiple ER_NO_SUCH_TABLE can be raised in case of views. + */ + return ((m_handled_errors > 0) && (m_unhandled_errors == 0)); +} + + TABLE *unused_tables; /* Used by mysql_test */ HASH open_cache; /* Used by mysql_test */ static HASH table_def_cache; @@ -41,7 +93,6 @@ static int open_unireg_entry(THD *thd, TABLE *entry, TABLE_LIST *table_list, char *cache_key, uint cache_key_length, MEM_ROOT *mem_root, uint flags); static void free_cache_entry(TABLE *entry); -static void mysql_rm_tmp_tables(void); static bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias, uint db_stat, uint prgflag, uint ha_open_flags, TABLE *outparam, @@ -64,7 +115,6 @@ extern "C" byte *table_cache_key(const byte *record,uint *length, bool table_cache_init(void) { - mysql_rm_tmp_tables(); return hash_init(&open_cache, &my_charset_bin, table_cache_size+16, 0, 0,table_cache_key, (hash_free_key) free_cache_entry, 0) != 0; @@ -1064,9 +1114,7 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) handled either before writing a query log event (inside binlog_query()) or when preparing a pending event. */ -#ifdef HAVE_ROW_BASED_REPLICATION thd->binlog_flush_pending_rows_event(TRUE); -#endif /*HAVE_ROW_BASED_REPLICATION*/ mysql_unlock_tables(thd, thd->lock); thd->lock=0; } @@ -1083,11 +1131,17 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) if (!thd->active_transaction()) thd->transaction.xid_state.xid.null(); - /* VOID(pthread_sigmask(SIG_SETMASK,&thd->block_signals,NULL)); */ if (!lock_in_use) VOID(pthread_mutex_lock(&LOCK_open)); - DBUG_PRINT("info", ("thd->open_tables: %p", thd->open_tables)); + DBUG_PRINT("info", ("thd->open_tables: 0x%lx", (long) thd->open_tables)); + + /* + End open index scans and table scans and remove references to the tables + from the handler tables hash. After this preparation it is safe to close + the tables. + */ + mysql_ha_mark_tables_for_reopen(thd, thd->open_tables); found_old_table= 0; while (thd->open_tables) @@ -1140,12 +1194,7 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) } else { - if (table->s->flush_version != flush_version) - { - table->s->flush_version= flush_version; - table->file->extra(HA_EXTRA_FLUSH); - } - // Free memory and reset for next loop + /* Free memory and reset for next loop */ table->file->ha_reset(); table->in_use=0; if (unused_tables) @@ -1177,6 +1226,16 @@ static inline uint tmpkeyval(THD *thd, TABLE *table) void close_temporary_tables(THD *thd) { TABLE *table; + TABLE *next; + /* + TODO: 5.1 maintains prev link in temporary_tables + double-linked list so we could fix it. But it is not necessary + at this time when the list is being destroyed + */ + TABLE *prev_table; + /* Assume thd->options has OPTION_QUOTE_SHOW_CREATE */ + bool was_quote_show= TRUE; + if (!thd->temporary_tables) return; @@ -1192,20 +1251,16 @@ void close_temporary_tables(THD *thd) return; } - TABLE *next, - *prev_table /* TODO: 5.1 maintaines prev link in temporary_tables - double-linked list so we could fix it. But it is not necessary - at this time when the list is being destroyed */; - bool was_quote_show= true; /* to assume thd->options has OPTION_QUOTE_SHOW_CREATE */ - // Better add "if exists", in case a RESET MASTER has been done + /* Better add "if exists", in case a RESET MASTER has been done */ const char stub[]= "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS "; uint stub_len= sizeof(stub) - 1; char buf[256]; - memcpy(buf, stub, stub_len); String s_query= String(buf, sizeof(buf), system_charset_info); - bool found_user_tables= false; + bool found_user_tables= FALSE; LINT_INIT(next); + memcpy(buf, stub, stub_len); + /* insertion sort of temp tables by pseudo_thread_id to build ordered list of sublists of equal pseudo_thread_id @@ -1246,7 +1301,7 @@ void close_temporary_tables(THD *thd) /* We always quote db,table names though it is slight overkill */ if (found_user_tables && - !(was_quote_show= (thd->options & OPTION_QUOTE_SHOW_CREATE))) + !(was_quote_show= test(thd->options & OPTION_QUOTE_SHOW_CREATE))) { thd->options |= OPTION_QUOTE_SHOW_CREATE; } @@ -1256,10 +1311,13 @@ void close_temporary_tables(THD *thd) { if (is_user_table(table)) { + my_thread_id save_pseudo_thread_id= thd->variables.pseudo_thread_id; /* Set pseudo_thread_id to be that of the processed table */ thd->variables.pseudo_thread_id= tmpkeyval(thd, table); - /* Loop forward through all tables within the sublist of - common pseudo_thread_id to create single DROP query */ + /* + Loop forward through all tables within the sublist of + common pseudo_thread_id to create single DROP query. + */ for (s_query.length(stub_len); table && is_user_table(table) && tmpkeyval(thd, table) == thd->variables.pseudo_thread_id; @@ -1285,16 +1343,18 @@ void close_temporary_tables(THD *thd) 0, FALSE); thd->variables.character_set_client= cs_save; /* - Imagine the thread had created a temp table, then was doing a SELECT, and - the SELECT was killed. Then it's not clever to mark the statement above as - "killed", because it's not really a statement updating data, and there - are 99.99% chances it will succeed on slave. - If a real update (one updating a persistent table) was killed on the - master, then this real update will be logged with error_code=killed, - rightfully causing the slave to stop. + Imagine the thread had created a temp table, then was doing a + SELECT, and the SELECT was killed. Then it's not clever to + mark the statement above as "killed", because it's not really + a statement updating data, and there are 99.99% chances it + will succeed on slave. If a real update (one updating a + persistent table) was killed on the master, then this real + update will be logged with error_code=killed, rightfully + causing the slave to stop. */ qinfo.error_code= 0; mysql_bin_log.write(&qinfo); + thd->variables.pseudo_thread_id= save_pseudo_thread_id; } else { @@ -1303,7 +1363,7 @@ void close_temporary_tables(THD *thd) } } if (!was_quote_show) - thd->options &= ~OPTION_QUOTE_SHOW_CREATE; /* restore option */ + thd->options&= ~OPTION_QUOTE_SHOW_CREATE; /* restore option */ thd->temporary_tables=0; } @@ -1350,6 +1410,7 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, thd thread handle table table which should be checked table_list list of tables + check_alias whether to check tables' aliases NOTE: to exclude derived tables from check we use following mechanism: a) during derived table processing set THD::derived_tables_processing @@ -1365,6 +1426,10 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, Also SELECT::exclude_from_table_unique_test used to exclude from check tables of main SELECT of multi-delete and multi-update + We also skip tables with TABLE_LIST::prelocking_placeholder set, + because we want to allow SELECTs from them, and their modification + will rise the error anyway. + TODO: when we will have table/view change detection we can do this check only once for PS/SP @@ -1373,10 +1438,11 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, 0 if table is unique */ -TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list) +TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, + bool check_alias) { TABLE_LIST *res; - const char *d_name, *t_name; + const char *d_name, *t_name, *t_alias; DBUG_ENTER("unique_table"); DBUG_PRINT("enter", ("table alias: %s", table->alias)); @@ -1404,6 +1470,7 @@ TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list) } d_name= table->db; t_name= table->table_name; + t_alias= table->alias; DBUG_PRINT("info", ("real table: %s.%s", d_name, t_name)); for (;;) @@ -1411,12 +1478,16 @@ TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list) if (((! (res= find_table_in_global_list(table_list, d_name, t_name))) && (! (res= mysql_lock_have_duplicate(thd, table, table_list)))) || ((!res->table || res->table != table->table) && - res->select_lex && !res->select_lex->exclude_from_table_unique_test)) + (!check_alias || !(lower_case_table_names ? + my_strcasecmp(files_charset_info, t_alias, res->alias) : + strcmp(t_alias, res->alias))) && + res->select_lex && !res->select_lex->exclude_from_table_unique_test && + !res->prelocking_placeholder)) break; /* - If we found entry of this table or or table of SELECT which already + If we found entry of this table or table of SELECT which already processed in derived table or top select of multi-update/multi-delete - (exclude_from_table_unique_test). + (exclude_from_table_unique_test) or prelocking placeholder. */ table_list= res->next_global; DBUG_PRINT("info", @@ -1507,9 +1578,15 @@ TABLE *find_temporary_table(THD *thd, TABLE_LIST *table_list) { if (table->s->table_cache_key.length == key_length && !memcmp(table->s->table_cache_key.str, key, key_length)) + { + DBUG_PRINT("info", + ("Found table. server_id: %u pseudo_thread_id: %lu", + (uint) thd->server_id, + (ulong) thd->variables.pseudo_thread_id)); DBUG_RETURN(table); + } } - DBUG_RETURN(0); // Not a temporary table + DBUG_RETURN(0); // Not a temporary table } @@ -1765,7 +1842,6 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) share= table->s; share->version=0; - share->flush_version=0; table->in_use = thd; check_unused(); table->next = thd->open_tables; @@ -1833,6 +1909,13 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, key_length= (create_table_def_key(thd, key, table_list, 1) - TMP_TABLE_KEY_EXTRA); + /* + Unless requested otherwise, try to resolve this table in the list + of temporary tables of this thread. In MySQL temporary tables + are always thread-local and "shadow" possible base tables with the + same name. This block implements the behaviour. + TODO: move this block into a separate function. + */ if (!table_list->skip_temporary) { for (table= thd->temporary_tables; table ; table=table->next) @@ -1842,9 +1925,19 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, !memcmp(table->s->table_cache_key.str, key, key_length + TMP_TABLE_KEY_EXTRA)) { + /* + We're trying to use the same temporary table twice in a query. + Right now we don't support this because a temporary table + is always represented by only one TABLE object in THD, and + it can not be cloned. Emit an error for an unsupported behaviour. + */ if (table->query_id == thd->query_id || thd->prelocked_mode && table->query_id) { + DBUG_PRINT("error", + ("query_id: %lu server_id: %u pseudo_thread_id: %lu", + (ulong) table->query_id, (uint) thd->server_id, + (ulong) thd->variables.pseudo_thread_id)); my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias); DBUG_RETURN(0); } @@ -1857,6 +1950,13 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } } + /* + The table is not temporary - if we're in pre-locked or LOCK TABLES + mode, let's try to find the requested table in the list of pre-opened + and locked tables. If the table is not there, return an error - we can't + open not pre-opened tables in pre-locked/LOCK TABLES mode. + TODO: move this block into a separate function. + */ if (!(flags & MYSQL_OPEN_IGNORE_LOCKED_TABLES) && (thd->locked_tables || thd->prelocked_mode)) { // Using table locks @@ -1928,7 +2028,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, goto reset; } /* - is it view? + Is this table a view and not a base table? (it is work around to allow to open view with locked tables, real fix will be made after definition cache will be made) */ @@ -1956,12 +2056,39 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, VOID(pthread_mutex_unlock(&LOCK_open)); } } - my_error(ER_TABLE_NOT_LOCKED, MYF(0), alias); + if ((thd->locked_tables) && (thd->locked_tables->lock_count > 0)) + my_error(ER_TABLE_NOT_LOCKED, MYF(0), alias); + else + my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias); DBUG_RETURN(0); } + /* + Non pre-locked/LOCK TABLES mode, and the table is not temporary: + this is the normal use case. + Now we should: + - try to find the table in the table cache. + - if one of the discovered TABLE instances is name-locked + (table->s->version == 0) or some thread has started FLUSH TABLES + (refresh_version > table->s->version), back off -- we have to wait + until no one holds a name lock on the table. + - if there is no such TABLE in the name cache, read the table definition + and insert it into the cache. + We perform all of the above under LOCK_open which currently protects + the open cache (also known as table cache) and table definitions stored + on disk. + */ + VOID(pthread_mutex_lock(&LOCK_open)); + /* + If it's the first table from a list of tables used in a query, + remember refresh_version (the version of open_cache state). + If the version changes while we're opening the remaining tables, + we will have to back off, close all the tables opened-so-far, + and try to reopen them. + Note: refresh_version is currently changed only during FLUSH TABLES. + */ if (!thd->open_tables) thd->version=refresh_version; else if ((thd->version != refresh_version) && @@ -1978,6 +2105,16 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, if (thd->handler_tables) mysql_ha_flush(thd, (TABLE_LIST*) NULL, MYSQL_HA_REOPEN_ON_USAGE, TRUE); + /* + Actually try to find the table in the open_cache. + The cache may contain several "TABLE" instances for the same + physical table. The instances that are currently "in use" by + some thread have their "in_use" member != NULL. + There is no good reason for having more than one entry in the + hash for the same physical table, except that we use this as + an implicit "pending locks queue" - see + wait_for_locked_table_names for details. + */ for (table= (TABLE*) hash_first(&open_cache, (byte*) key, key_length, &state); table && table->in_use ; @@ -1987,6 +2124,21 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, /* Here we flush tables marked for flush. However we never flush log tables here. They are flushed only on FLUSH LOGS. + Normally, table->s->version contains the value of + refresh_version from the moment when this table was + (re-)opened and added to the cache. + If since then we did (or just started) FLUSH TABLES + statement, refresh_version has been increased. + For "name-locked" TABLE instances, table->s->version is set + to 0 (see lock_table_name for details). + In case there is a pending FLUSH TABLES or a name lock, we + need to back off and re-start opening tables. + If we do not back off now, we may dead lock in case of lock + order mismatch with some other thread: + c1: name lock t1; -- sort of exclusive lock + c2: open t2; -- sort of shared lock + c1: name lock t2; -- blocks + c2: open t1; -- blocks */ if (table->s->version != refresh_version && !table->s->log_table) { @@ -2002,16 +2154,35 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } /* - There is a refresh in progress for this table - Wait until the table is freed or the thread is killed. + Back off, part 1: mark the table as "unused" for the + purpose of name-locking by setting table->db_stat to 0. Do + that only for the tables in this thread that have an old + table->s->version (this is an optimization (?)). + table->db_stat == 0 signals wait_for_locked_table_names + that the tables in question are not used any more. See + table_is_used call for details. */ close_old_data_files(thd,thd->open_tables,0,0); + /* + Back-off part 2: try to avoid "busy waiting" on the table: + if the table is in use by some other thread, we suspend + and wait till the operation is complete: when any + operation that juggles with table->s->version completes, + it broadcasts COND_refresh condition variable. + */ if (table->in_use != thd) + { + /* wait_for_conditionwill unlock LOCK_open for us */ wait_for_condition(thd, &LOCK_open, &COND_refresh); + } else { VOID(pthread_mutex_unlock(&LOCK_open)); } + /* + There is a refresh in progress for this table. + Signal the caller that it has to try again. + */ if (refresh) *refresh=1; DBUG_RETURN(0); @@ -2019,6 +2190,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } if (table) { + /* Unlink the table from "unused_tables" list. */ if (table == unused_tables) { // First unused unused_tables=unused_tables->next; // Remove from link @@ -2031,6 +2203,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } else { + /* Insert a new TABLE instance into the open cache */ int error; /* Free cache if too big */ while (open_cache.records > table_cache_size && unused_tables) @@ -2064,7 +2237,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_RETURN(0); // VIEW } - DBUG_PRINT("info", ("inserting table %p into the cache", table)); + DBUG_PRINT("info", ("inserting table 0x%lx into the cache", (long) table)); VOID(my_hash_insert(&open_cache,(byte*) table)); } @@ -2105,6 +2278,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table->file->ft_handler= 0; if (table->timestamp_field) table->timestamp_field_type= table->timestamp_field->get_auto_set_type(); + table->pos_in_table_list= table_list; table_list->updatable= 1; // It is not derived table nor non-updatable VIEW table->clear_column_bitmaps(); DBUG_ASSERT(table->key_read == 0); @@ -2394,7 +2568,7 @@ bool table_is_used(TABLE *table, bool wait_for_name_lock) { DBUG_PRINT("info", ("share: 0x%lx locked_by_logger: %d " "locked_by_flush: %d locked_by_name: %d " - "db_stat: %u version: %u", + "db_stat: %u version: %lu", (ulong) search->s, search->locked_by_logger, search->locked_by_flush, search->locked_by_name, search->db_stat, @@ -2838,6 +3012,8 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) MEM_ROOT new_frm_mem; /* Also used for indicating that prelocking is need */ TABLE_LIST **query_tables_last_own; + bool safe_to_ignore_table; + DBUG_ENTER("open_tables"); /* temporary mem_root for new .frm parsing. @@ -2884,8 +3060,13 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) } } + /* + For every table in the list of tables to open, try to find or open + a table. + */ for (tables= *start; tables ;tables= tables->next_global) { + safe_to_ignore_table= FALSE; // 'FALSE', as per coding style /* Ignore placeholders for derived tables. After derived tables processing, link to created temporary table will be put here. @@ -2898,6 +3079,12 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) goto process_view_routines; continue; } + /* + If this TABLE_LIST object is a placeholder for an information_schema + table, create a temporary table to represent the information_schema + table in the query. Do not fill it yet - will be filled during + execution. + */ if (tables->schema_table) { if (!mysql_schema_table(thd, thd->lex, tables)) @@ -2905,9 +3092,32 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) DBUG_RETURN(-1); } (*counter)++; - - if (!tables->table && - !(tables->table= open_table(thd, tables, &new_frm_mem, &refresh, flags))) + + /* + Not a placeholder: must be a base table or a view, and the table is + not opened yet. Try to open the table. + */ + if (!tables->table) + { + if (tables->prelocking_placeholder) + { + /* + For the tables added by the pre-locking code, attempt to open + the table but fail silently if the table does not exist. + The real failure will occur when/if a statement attempts to use + that table. + */ + Prelock_error_handler prelock_handler; + thd->push_internal_handler(& prelock_handler); + tables->table= open_table(thd, tables, &new_frm_mem, &refresh, flags); + thd->pop_internal_handler(); + safe_to_ignore_table= prelock_handler.safely_trapped_errors(); + } + else + tables->table= open_table(thd, tables, &new_frm_mem, &refresh, flags); + } + + if (!tables->table) { free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); @@ -2958,6 +3168,14 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) close_tables_for_reopen(thd, start); goto restart; } + + if (safe_to_ignore_table) + { + DBUG_PRINT("info", ("open_table: ignoring table '%s'.'%s'", + tables->db, tables->alias)); + continue; + } + result= -1; // Fatal error break; } @@ -3012,7 +3230,7 @@ process_view_routines: { /* Serious error during reading stored routines from mysql.proc table. - Something's wrong with the table or its contents, and an error has + Something is wrong with the table or its contents, and an error has been emitted; we must abort. */ result= -1; @@ -3261,7 +3479,7 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags) static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table) { for (; table; table= table->next_global) - if (!table->placeholder() && !table->schema_table) + if (!table->placeholder()) table->table->query_id= 0; } @@ -3307,13 +3525,11 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) *need_reopen= FALSE; -#ifdef HAVE_ROW_BASED_REPLICATION /* CREATE ... SELECT UUID() locks no tables, we have to test here. */ if (thd->lex->binlog_row_based_if_mixed) thd->set_current_stmt_binlog_row_based_if_mixed(); -#endif /*HAVE_ROW_BASED_REPLICATION*/ if (!tables && !thd->lex->requires_prelocking()) DBUG_RETURN(0); @@ -3336,7 +3552,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) DBUG_RETURN(-1); for (table= tables; table; table= table->next_global) { - if (!table->placeholder() && !table->schema_table) + if (!table->placeholder()) *(ptr++)= table->table; } @@ -3345,7 +3561,6 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) { thd->in_lock_tables=1; thd->options|= OPTION_TABLE_LOCK; -#ifdef HAVE_ROW_BASED_REPLICATION /* If we have >= 2 different tables to update with auto_inc columns, statement-based binlogging won't work. We can solve this problem in @@ -3357,7 +3572,6 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) thd->lex->binlog_row_based_if_mixed= TRUE; thd->set_current_stmt_binlog_row_based_if_mixed(); } -#endif } if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start), @@ -3391,7 +3605,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) for (table= tables; table != first_not_own; table= table->next_global) { - if (!table->placeholder() && !table->schema_table) + if (!table->placeholder()) { table->table->query_id= thd->query_id; if (check_lock_and_start_stmt(thd, table->table, table->lock_type)) @@ -3418,7 +3632,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) TABLE_LIST *first_not_own= thd->lex->first_not_own_table(); for (table= tables; table != first_not_own; table= table->next_global) { - if (!table->placeholder() && !table->schema_table && + if (!table->placeholder() && check_lock_and_start_stmt(thd, table->table, table->lock_type)) { ha_rollback_stmt(thd); @@ -3498,8 +3712,11 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, uint key_length; TABLE_LIST table_list; DBUG_ENTER("open_temporary_table"); - DBUG_PRINT("enter", ("table: '%s'.'%s' path: '%s'", - db, table_name, path)); + DBUG_PRINT("enter", + ("table: '%s'.'%s' path: '%s' server_id: %u " + "pseudo_thread_id: %lu", + db, table_name, path, + (uint) thd->server_id, (ulong) thd->variables.pseudo_thread_id)); table_list.db= (char*) db; table_list.table_name= (char*) table_name; @@ -3548,6 +3765,7 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, if (thd->slave_thread) slave_open_temp_tables++; } + tmp_table->pos_in_table_list= 0; DBUG_RETURN(tmp_table); } @@ -3754,7 +3972,7 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, { List_iterator_fast<Natural_join_column> field_it(*(table_ref->join_columns)); - Natural_join_column *nj_col; + Natural_join_column *nj_col, *curr_nj_col; Field *found_field; Query_arena *arena, backup; DBUG_ENTER("find_field_in_natural_join"); @@ -3766,18 +3984,26 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, LINT_INIT(arena); LINT_INIT(found_field); - for (;;) + for (nj_col= NULL, curr_nj_col= field_it++; curr_nj_col; + curr_nj_col= field_it++) { - if (!(nj_col= field_it++)) - DBUG_RETURN(NULL); - - if (!my_strcasecmp(system_charset_info, nj_col->name(), name)) - break; + if (!my_strcasecmp(system_charset_info, curr_nj_col->name(), name)) + { + if (nj_col) + { + my_error(ER_NON_UNIQ_ERROR, MYF(0), name, thd->where); + DBUG_RETURN(NULL); + } + nj_col= curr_nj_col; + } } + if (!nj_col) + DBUG_RETURN(NULL); if (nj_col->view_field) { Item *item; + LINT_INIT(arena); if (register_tree_change) arena= thd->activate_stmt_arena_if_needed(&backup); /* @@ -3785,6 +4011,19 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, column reference. See create_view_field() for details. */ item= nj_col->create_item(thd); + /* + *ref != NULL means that *ref contains the item that we need to + replace. If the item was aliased by the user, set the alias to + the replacing item. + We need to set alias on both ref itself and on ref real item. + */ + if (*ref && !(*ref)->is_autogenerated_name) + { + item->set_name((*ref)->name, (*ref)->name_length, + system_charset_info); + item->real_item()->set_name((*ref)->name, (*ref)->name_length, + system_charset_info); + } if (register_tree_change && arena) thd->restore_active_arena(arena, &backup); @@ -3948,6 +4187,9 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, { Field *fld; DBUG_ENTER("find_field_in_table_ref"); + DBUG_ASSERT(table_list->alias); + DBUG_ASSERT(name); + DBUG_ASSERT(item_name); DBUG_PRINT("enter", ("table: '%s' field name: '%s' item name: '%s' ref 0x%lx", table_list->alias, name, item_name, (ulong) ref)); @@ -4213,6 +4455,12 @@ find_field_in_tables(THD *thd, Item_ident *item, { if (found == WRONG_GRANT) return (Field*) 0; + + /* + Only views fields should be marked as dependent, not an underlying + fields. + */ + if (!table_ref->belong_to_view) { SELECT_LEX *current_sel= thd->lex->current_select; SELECT_LEX *last_select= table_ref->select_lex; @@ -4335,10 +4583,13 @@ find_field_in_tables(THD *thd, Item_ident *item, return not_found_item, report other errors, return 0 IGNORE_ERRORS Do not report errors, return 0 if error - unaliased Set to true if item is field which was found - by original field name and not by its alias - in item list. Set to false otherwise. - + resolution Set to the resolution type if the item is found + (it says whether the item is resolved + against an alias name, + or as a field name without alias, + or as a field hidden by alias, + or ignoring alias) + RETURN VALUES 0 Item is not found or item is not unique, error message is reported @@ -4354,7 +4605,8 @@ Item **not_found_item= (Item**) 0x1; Item ** find_item_in_list(Item *find, List<Item> &items, uint *counter, - find_item_error_report_type report_error, bool *unaliased) + find_item_error_report_type report_error, + enum_resolution_type *resolution) { List_iterator<Item> li(items); Item **found=0, **found_unaliased= 0, *item; @@ -4368,10 +4620,9 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, */ bool is_ref_by_name= 0; uint unaliased_counter; - LINT_INIT(unaliased_counter); // Dependent on found_unaliased - *unaliased= FALSE; + *resolution= NOT_RESOLVED; is_ref_by_name= (find->type() == Item::FIELD_ITEM || find->type() == Item::REF_ITEM); @@ -4438,63 +4689,77 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, } found_unaliased= li.ref(); unaliased_counter= i; + *resolution= RESOLVED_IGNORING_ALIAS; if (db_name) break; // Perfect match } } - else if (!my_strcasecmp(system_charset_info, item_field->name, - field_name)) - { - /* - If table name was not given we should scan through aliases - (or non-aliased fields) first. We are also checking unaliased - name of the field in then next else-if, to be able to find - instantly field (hidden by alias) if no suitable alias (or - non-aliased field) was found. - */ - if (found) - { - if ((*found)->eq(item, 0)) - continue; // Same field twice - if (report_error != IGNORE_ERRORS) - my_error(ER_NON_UNIQ_ERROR, MYF(0), - find->full_name(), current_thd->where); - return (Item**) 0; - } - found= li.ref(); - *counter= i; - } - else if (!my_strcasecmp(system_charset_info, item_field->field_name, - field_name)) + else { - /* - We will use un-aliased field or react on such ambiguities only if - we won't be able to find aliased field. - Again if we have ambiguity with field outside of select list - we should prefer fields from select list. - */ - if (found_unaliased) + int fname_cmp= my_strcasecmp(system_charset_info, + item_field->field_name, + field_name); + if (!my_strcasecmp(system_charset_info, + item_field->name,field_name)) { - if ((*found_unaliased)->eq(item, 0)) - continue; // Same field twice - found_unaliased_non_uniq= 1; + /* + If table name was not given we should scan through aliases + and non-aliased fields first. We are also checking unaliased + name of the field in then next else-if, to be able to find + instantly field (hidden by alias) if no suitable alias or + non-aliased field was found. + */ + if (found) + { + if ((*found)->eq(item, 0)) + continue; // Same field twice + if (report_error != IGNORE_ERRORS) + my_error(ER_NON_UNIQ_ERROR, MYF(0), + find->full_name(), current_thd->where); + return (Item**) 0; + } + found= li.ref(); + *counter= i; + *resolution= fname_cmp ? RESOLVED_AGAINST_ALIAS: + RESOLVED_WITH_NO_ALIAS; } - else + else if (!fname_cmp) { + /* + We will use non-aliased field or react on such ambiguities only if + we won't be able to find aliased field. + Again if we have ambiguity with field outside of select list + we should prefer fields from select list. + */ + if (found_unaliased) + { + if ((*found_unaliased)->eq(item, 0)) + continue; // Same field twice + found_unaliased_non_uniq= 1; + } found_unaliased= li.ref(); unaliased_counter= i; } } } - else if (!table_name && (find->eq(item,0) || - is_ref_by_name && find->name && item->name && - !my_strcasecmp(system_charset_info, - item->name,find->name))) - { - found= li.ref(); - *counter= i; - break; - } + else if (!table_name) + { + if (is_ref_by_name && find->name && item->name && + !my_strcasecmp(system_charset_info,item->name,find->name)) + { + found= li.ref(); + *counter= i; + *resolution= RESOLVED_AGAINST_ALIAS; + break; + } + else if (find->eq(item,0)) + { + found= li.ref(); + *counter= i; + *resolution= RESOLVED_IGNORING_ALIAS; + break; + } + } } if (!found) { @@ -4509,7 +4774,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, { found= found_unaliased; *counter= unaliased_counter; - *unaliased= TRUE; + *resolution= RESOLVED_BEHIND_ALIAS; } } if (found) @@ -4655,9 +4920,16 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, { bool found= FALSE; const char *field_name_1; + /* true if field_name_1 is a member of using_fields */ + bool is_using_column_1; if (!(nj_col_1= it_1.get_or_create_column_ref(leaf_1))) goto err; field_name_1= nj_col_1->name(); + is_using_column_1= using_fields && + test_if_string_in_list(field_name_1, using_fields); + DBUG_PRINT ("info", ("field_name_1=%s.%s", + nj_col_1->table_name() ? nj_col_1->table_name() : "", + field_name_1)); /* Find a field with the same name in table_ref_2. @@ -4674,6 +4946,10 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, if (!(cur_nj_col_2= it_2.get_or_create_column_ref(leaf_2))) goto err; cur_field_name_2= cur_nj_col_2->name(); + DBUG_PRINT ("info", ("cur_field_name_2=%s.%s", + cur_nj_col_2->table_name() ? + cur_nj_col_2->table_name() : "", + cur_field_name_2)); /* Compare the two columns and check for duplicate common fields. @@ -4681,10 +4957,16 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, table_ref_2 (then found == TRUE), or if a field in table_ref_2 was already matched by some previous field in table_ref_1 (then cur_nj_col_2->is_common == TRUE). + Note that it is too early to check the columns outside of the + USING list for ambiguity because they are not actually "referenced" + here. These columns must be checked only on unqualified reference + by name (e.g. in SELECT list). */ if (!my_strcasecmp(system_charset_info, field_name_1, cur_field_name_2)) { - if (found || cur_nj_col_2->is_common) + DBUG_PRINT ("info", ("match c1.is_common=%d", nj_col_1->is_common)); + if (cur_nj_col_2->is_common || + (found && (!using_fields || is_using_column_1))) { my_error(ER_NON_UNIQ_ERROR, MYF(0), field_name_1, thd->where); goto err; @@ -4710,9 +4992,7 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, clause (if present), mark them as common fields, and add a new equi-join condition to the ON clause. */ - if (nj_col_2 && - (!using_fields || - test_if_string_in_list(field_name_1, using_fields))) + if (nj_col_2 && (!using_fields ||is_using_column_1)) { Item *item_1= nj_col_1->create_item(thd); Item *item_2= nj_col_2->create_item(thd); @@ -4767,6 +5047,13 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, eq_cond); nj_col_1->is_common= nj_col_2->is_common= TRUE; + DBUG_PRINT ("info", ("%s.%s and %s.%s are common", + nj_col_1->table_name() ? + nj_col_1->table_name() : "", + nj_col_1->name(), + nj_col_2->table_name() ? + nj_col_2->table_name() : "", + nj_col_2->name())); if (field_1) { @@ -5269,6 +5556,7 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, enum_mark_columns save_mark_used_columns= thd->mark_used_columns; nesting_map save_allow_sum_func= thd->lex->allow_sum_func; List_iterator<Item> it(fields); + bool save_is_item_list_lookup; DBUG_ENTER("setup_fields"); thd->mark_used_columns= mark_used_columns; @@ -5276,6 +5564,8 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, if (allow_sum_func) thd->lex->allow_sum_func|= 1 << thd->lex->current_select->nest_level; thd->where= THD::DEFAULT_WHERE; + save_is_item_list_lookup= thd->lex->current_select->is_item_list_lookup; + thd->lex->current_select->is_item_list_lookup= 0; /* To prevent fail on forward lookup we fill it with zerows, @@ -5292,11 +5582,13 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, bzero(ref_pointer_array, sizeof(Item *) * fields.elements); Item **ref= ref_pointer_array; + thd->lex->current_select->cur_pos_in_select_list= 0; while ((item= it++)) { if (!item->fixed && item->fix_fields(thd, it.ref()) || (item= *(it.ref()))->check_cols(1)) { + thd->lex->current_select->is_item_list_lookup= save_is_item_list_lookup; thd->lex->allow_sum_func= save_allow_sum_func; thd->mark_used_columns= save_mark_used_columns; DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); @@ -5308,7 +5600,11 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, sum_func_list) item->split_sum_func(thd, ref_pointer_array, *sum_func_list); thd->used_tables|= item->used_tables(); + thd->lex->current_select->cur_pos_in_select_list++; } + thd->lex->current_select->is_item_list_lookup= save_is_item_list_lookup; + thd->lex->current_select->cur_pos_in_select_list= UNDEF_POS; + thd->lex->allow_sum_func= save_allow_sum_func; thd->mark_used_columns= save_mark_used_columns; DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); @@ -5381,21 +5677,8 @@ bool setup_tables(THD *thd, Name_resolution_context *context, uint tablenr= 0; DBUG_ENTER("setup_tables"); - /* - Due to the various call paths that lead to setup_tables() it may happen - that context->table_list and context->first_name_resolution_table can be - NULL (this is typically done when creating TABLE_LISTs internally). - TODO: - Investigate all cases when this my happen, initialize the name resolution - context correctly in all those places, and remove the context reset below. - */ - if (!context->table_list || !context->first_name_resolution_table) - { - /* Test whether the context is in a consistent state. */ - DBUG_ASSERT(!context->first_name_resolution_table && !context->table_list); - context->table_list= context->first_name_resolution_table= tables; - } - + DBUG_ASSERT ((select_insert && !tables->next_name_resolution_table) || !tables || + (context->table_list && context->first_name_resolution_table)); /* this is used for INSERT ... SELECT. For select we setup tables except first (and its underlying tables) @@ -5429,7 +5712,12 @@ bool setup_tables(THD *thd, Name_resolution_context *context, get_key_map_from_key_list(&map, table, table_list->use_index); if (map.is_set_all()) DBUG_RETURN(1); - table->keys_in_use_for_query=map; + /* + Don't introduce keys in keys_in_use_for_query that weren't there + before. FORCE/USE INDEX should not add keys, it should only remove + all keys except the key(s) specified in the hint. + */ + table->keys_in_use_for_query.intersect(map); } if (table_list->ignore_index) { @@ -5808,6 +6096,8 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, */ bool it_is_update= (select_lex == &thd->lex->select_lex) && thd->lex->which_check_option_applicable(); + bool save_is_item_list_lookup= select_lex->is_item_list_lookup; + select_lex->is_item_list_lookup= 0; DBUG_ENTER("setup_conds"); if (select_lex->conds_processed_with_permanent_arena || @@ -5883,9 +6173,11 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, select_lex->where= *conds; select_lex->conds_processed_with_permanent_arena= 1; } + thd->lex->current_select->is_item_list_lookup= save_is_item_list_lookup; DBUG_RETURN(test(thd->net.report_error)); err_no_arena: + select_lex->is_item_list_lookup= save_is_item_list_lookup; DBUG_RETURN(1); } @@ -6048,14 +6340,21 @@ fill_record_n_invoke_before_triggers(THD *thd, Field **ptr, } -static void mysql_rm_tmp_tables(void) +my_bool mysql_rm_tmp_tables(void) { uint i, idx; - char filePath[FN_REFLEN], *tmpdir; + char filePath[FN_REFLEN], *tmpdir, filePathCopy[FN_REFLEN]; MY_DIR *dirp; FILEINFO *file; + TABLE_SHARE share; + THD *thd; DBUG_ENTER("mysql_rm_tmp_tables"); + if (!(thd= new THD)) + DBUG_RETURN(1); + thd->thread_stack= (char*) &thd; + thd->store_globals(); + for (i=0; i<=mysql_tmpdir_list.max; i++) { tmpdir=mysql_tmpdir_list.list[i]; @@ -6076,13 +6375,40 @@ static void mysql_rm_tmp_tables(void) if (!bcmp(file->name,tmp_file_prefix,tmp_file_prefix_length)) { - sprintf(filePath,"%s%c%s",tmpdir,FN_LIBCHAR,file->name); - VOID(my_delete(filePath,MYF(MY_WME))); + char *ext= fn_ext(file->name); + uint ext_len= strlen(ext); + uint filePath_len= my_snprintf(filePath, sizeof(filePath), + "%s%c%s", tmpdir, FN_LIBCHAR, + file->name); + if (!bcmp(reg_ext, ext, ext_len)) + { + handler *handler_file= 0; + /* We should cut file extention before deleting of table */ + memcpy(filePathCopy, filePath, filePath_len - ext_len); + filePathCopy[filePath_len - ext_len]= 0; + init_tmp_table_share(&share, "", 0, "", filePathCopy); + if (!open_table_def(thd, &share, 0) && + ((handler_file= get_new_handler(&share, thd->mem_root, + share.db_type)))) + { + handler_file->delete_table(filePathCopy); + delete handler_file; + } + free_table_share(&share); + } + /* + File can be already deleted by tmp_table.file->delete_table(). + So we hide error messages which happnes during deleting of these + files(MYF(0)). + */ + VOID(my_delete(filePath, MYF(0))); } } my_dirend(dirp); } - DBUG_VOID_RETURN; + delete thd; + my_pthread_setspecific_ptr(THR_THD, 0); + DBUG_RETURN(0); } @@ -6558,6 +6884,8 @@ static bool has_two_write_locked_tables_with_auto_increment(TABLE_LIST *tables) { char *first_table_name= NULL, *first_db; + LINT_INIT(first_db); + for (TABLE_LIST *table= tables; table; table= table->next_global) { /* we must do preliminary checks as table->table may be NULL */ diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc index 27418c9f9fc..6f7bbda96de 100644 --- a/sql/sql_binlog.cc +++ b/sql/sql_binlog.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2005 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2005-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -12,7 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mysql_priv.h" #include "base64.h" @@ -79,9 +78,16 @@ void mysql_client_binlog_statement(THD* thd) char const *endptr= 0; int bytes_decoded= base64_decode(strptr, coded_len, buf, &endptr); +#ifndef HAVE_purify + /* + This debug printout should not be used for valgrind builds + since it will read from unassigned memory. + */ DBUG_PRINT("info", - ("bytes_decoded=%d; strptr=0x%lu; endptr=0x%lu ('%c':%d)", - bytes_decoded, strptr, endptr, *endptr, *endptr)); + ("bytes_decoded: %d strptr: 0x%lx endptr: 0x%lx ('%c':%d)", + bytes_decoded, (long) strptr, (long) endptr, *endptr, + *endptr)); +#endif if (bytes_decoded < 0) { @@ -107,8 +113,8 @@ void mysql_client_binlog_statement(THD* thd) order to be able to read exactly what is necessary. */ - DBUG_PRINT("info",("binlog base64 decoded_len=%d, bytes_decoded=%d", - decoded_len, bytes_decoded)); + DBUG_PRINT("info",("binlog base64 decoded_len: %lu bytes_decoded: %d", + (ulong) decoded_len, bytes_decoded)); /* Now we start to read events of the buffer, until there are no @@ -145,10 +151,17 @@ void mysql_client_binlog_statement(THD* thd) bufptr += event_len; DBUG_PRINT("info",("ev->get_type_code()=%d", ev->get_type_code())); - DBUG_PRINT("info",("bufptr+EVENT_TYPE_OFFSET=0x%lx", - bufptr+EVENT_TYPE_OFFSET)); - DBUG_PRINT("info", ("bytes_decoded=%d; bufptr=0x%lx; buf[EVENT_LEN_OFFSET]=%u", - bytes_decoded, bufptr, uint4korr(bufptr+EVENT_LEN_OFFSET))); +#ifndef HAVE_purify + /* + This debug printout should not be used for valgrind builds + since it will read from unassigned memory. + */ + DBUG_PRINT("info",("bufptr+EVENT_TYPE_OFFSET: 0x%lx", + (long) (bufptr+EVENT_TYPE_OFFSET))); + DBUG_PRINT("info", ("bytes_decoded: %d bufptr: 0x%lx buf[EVENT_LEN_OFFSET]: %lu", + bytes_decoded, (long) bufptr, + (ulong) uint4korr(bufptr+EVENT_LEN_OFFSET))); +#endif ev->thd= thd; /* We go directly to the application phase, since we don't need @@ -158,9 +171,9 @@ void mysql_client_binlog_statement(THD* thd) not used at all: the rli_fake instance is used only for error reporting. */ - if (int err= ev->apply_event(thd->rli_fake)) + if (IF_DBUG(int err= ) ev->apply_event(thd->rli_fake)) { - DBUG_PRINT("info", ("apply_event() - error=%d", error)); + DBUG_PRINT("info", ("apply_event() returned: %d", err)); /* TODO: Maybe a better error message since the BINLOG statement now contains several events. diff --git a/sql/sql_bitmap.h b/sql/sql_bitmap.h index 3a7fa4b661a..9a765120895 100644 --- a/sql/sql_bitmap.h +++ b/sql/sql_bitmap.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_builtin.cc.in b/sql/sql_builtin.cc.in index 18705aa3dfb..3becdbaccfe 100644 --- a/sql/sql_builtin.cc.in +++ b/sql/sql_builtin.cc.in @@ -1,3 +1,17 @@ +/* Copyright (C) 2006 MySQL 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 + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <mysql/plugin.h> diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index ee3b8aa79fe..c06d7161ec1 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -523,7 +522,8 @@ void Query_cache_query::init_n_lock() my_rwlock_init(&lock, NULL); lock_writing(); DBUG_PRINT("qcache", ("inited & locked query for block 0x%lx", - ((byte*) this)-ALIGN_SIZE(sizeof(Query_cache_block)))); + (long) (((byte*) this) - + ALIGN_SIZE(sizeof(Query_cache_block))))); DBUG_VOID_RETURN; } @@ -532,7 +532,8 @@ void Query_cache_query::unlock_n_destroy() { DBUG_ENTER("Query_cache_query::unlock_n_destroy"); DBUG_PRINT("qcache", ("destroyed & unlocked query for block 0x%lx", - ((byte*)this)-ALIGN_SIZE(sizeof(Query_cache_block)))); + (long) (((byte*) this) - + ALIGN_SIZE(sizeof(Query_cache_block))))); /* The following call is not needed on system where one can destroy an active semaphore @@ -698,6 +699,7 @@ void query_cache_abort(NET *net) void query_cache_end_of_result(THD *thd) { + Query_cache_block *query_block; DBUG_ENTER("query_cache_end_of_result"); /* See the comment on double-check locking usage above. */ @@ -713,13 +715,9 @@ void query_cache_end_of_result(THD *thd) if (unlikely(query_cache.query_cache_size == 0 || query_cache.flush_in_progress)) - { - STRUCT_UNLOCK(&query_cache.structure_guard_mutex); - DBUG_VOID_RETURN; - } + goto end; - Query_cache_block *query_block= ((Query_cache_block*) - thd->net.query_cache_query); + query_block= ((Query_cache_block*) thd->net.query_cache_query); if (query_block) { DUMP(&query_cache); @@ -738,27 +736,25 @@ void query_cache_end_of_result(THD *thd) header->query())); query_cache.wreck(__LINE__, ""); - STRUCT_UNLOCK(&query_cache.structure_guard_mutex); - - DBUG_VOID_RETURN; + /* + We do not need call of BLOCK_UNLOCK_WR(query_block); here because + query_cache.wreck() switched query cache off but left content + untouched for investigation (it is debugging method). + */ + goto end; } #endif header->found_rows(current_thd->limit_found_rows); header->result()->type= Query_cache_block::RESULT; header->writer(0); thd->net.query_cache_query= 0; + BLOCK_UNLOCK_WR(query_block); DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); - STRUCT_UNLOCK(&query_cache.structure_guard_mutex); - - BLOCK_UNLOCK_WR(query_block); - } - else - { - // Cache was flushed or resized and query was deleted => do nothing - STRUCT_UNLOCK(&query_cache.structure_guard_mutex); } +end: + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); DBUG_VOID_RETURN; } @@ -875,8 +871,8 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu", flags.character_set_client_num, flags.character_set_results_num, flags.collation_connection_num, - flags.limit, - (ulong)flags.time_zone, + (ulong) flags.limit, + (ulong) flags.time_zone, flags.sql_mode, flags.max_sort_length, flags.group_concat_max_len)); @@ -1046,19 +1042,18 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) while (sql[i]=='(') i++; - /* Test if the query is a SELECT (pre-space is removed in dispatch_command). First '/' looks like comment before command it is not - frequently appeared in real lihe, consequently we can + frequently appeared in real life, consequently we can check all such queries, too. */ if ((my_toupper(system_charset_info, sql[i]) != 'S' || my_toupper(system_charset_info, sql[i + 1]) != 'E' || my_toupper(system_charset_info, sql[i + 2]) != 'L') && - sql[0] != '/') + sql[i] != '/') { DBUG_PRINT("qcache", ("The statement is not a SELECT; Not cached")); goto err; @@ -1119,8 +1114,8 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu", flags.character_set_client_num, flags.character_set_results_num, flags.collation_connection_num, - flags.limit, - (ulong)flags.time_zone, + (ulong) flags.limit, + (ulong) flags.time_zone, flags.sql_mode, flags.max_sort_length, flags.group_concat_max_len)); @@ -1234,9 +1229,9 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu", if (engine_data != table->engine_data()) { DBUG_PRINT("qcache", - ("Handler require invalidation queries of %s.%s %lld-%lld", - table_list.db, table_list.alias, - engine_data, table->engine_data())); + ("Handler require invalidation queries of %s.%s %lu-%lu", + table_list.db, table_list.alias, + (ulong) engine_data, (ulong) table->engine_data())); invalidate_table((byte *) table->db(), table->key_length()); } else @@ -1257,10 +1252,10 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu", #ifndef EMBEDDED_LIBRARY do { - DBUG_PRINT("qcache", ("Results (len %lu, used %lu, headers %lu)", + DBUG_PRINT("qcache", ("Results (len: %lu used: %lu headers: %lu)", result_block->length, result_block->used, - result_block->headers_len()+ - ALIGN_SIZE(sizeof(Query_cache_result)))); + (ulong) (result_block->headers_len()+ + ALIGN_SIZE(sizeof(Query_cache_result))))); Query_cache_result *result = result_block->result(); if (net_real_write(&thd->net, result->data(), @@ -1766,8 +1761,18 @@ void Query_cache::free_cache() { DBUG_ENTER("Query_cache::free_cache"); if (query_cache_size > 0) - { flush_cache(); + /* + There may be two free_cache() calls in progress, because we + release 'structure_guard_mutex' in flush_cache(). When the second + flush_cache() wakes up from the wait on 'COND_flush_finished', the + first call to free_cache() has done its job. So we have to test + 'query_cache_size > 0' the second time to see if the cache wasn't + reset by other thread, or if it was reset and was re-enabled then. + If the cache was reset, then we have nothing to do here. + */ + if (query_cache_size > 0) + { #ifndef DBUG_OFF if (bins[0].free_blocks == 0) { @@ -1809,6 +1814,12 @@ void Query_cache::free_cache() flush_in_progress flag and releases the lock, so other threads may proceed skipping the cache as if it is disabled. Concurrent flushes are performed in turn. + + After flush_cache() call, the cache is flushed, all the freed + memory is accumulated in bin[0], and the 'structure_guard_mutex' + is locked. However, since we could release the mutex during + execution, the rest of the cache state could have been changed, + and should not be relied on. */ void Query_cache::flush_cache() @@ -2034,7 +2045,7 @@ Query_cache::append_result_data(Query_cache_block **current_block, { DBUG_ENTER("Query_cache::append_result_data"); DBUG_PRINT("qcache", ("append %lu bytes to 0x%lx query", - data_len, query_block)); + data_len, (long) query_block)); if (query_block->query()->add(data_len) > query_cache_limit) { @@ -2476,11 +2487,11 @@ Query_cache::insert_table(uint key_len, char *key, table_block->table()->engine_data() != engine_data) { DBUG_PRINT("qcache", - ("Handler require invalidation queries of %s.%s %lld-%lld", + ("Handler require invalidation queries of %s.%s %lu-%lu", table_block->table()->db(), table_block->table()->table(), - engine_data, - table_block->table()->engine_data())); + (ulong) engine_data, + (ulong) table_block->table()->engine_data())); /* as far as we delete all queries with this table, table block will be deleted, too @@ -2988,7 +2999,7 @@ static TABLE_COUNTER_TYPE process_and_count_tables(TABLE_LIST *tables_used, DBUG_PRINT("qcache", ("table: %s db: %s type: %u", tables_used->table->s->table_name.str, tables_used->table->s->db.str, - tables_used->table->s->db_type)); + tables_used->table->s->db_type->db_type)); if (tables_used->derived) { table_count--; @@ -3043,10 +3054,10 @@ Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex, OPTION_TO_QUERY_CACHE))) && lex->safe_to_cache_query) { - DBUG_PRINT("qcache", ("options %lx %lx, type %u", - OPTION_TO_QUERY_CACHE, - lex->select_lex.options, - (int) thd->variables.query_cache_type)); + DBUG_PRINT("qcache", ("options: %lx %lx type: %u", + (long) OPTION_TO_QUERY_CACHE, + (long) lex->select_lex.options, + (int) thd->variables.query_cache_type)); if (!(table_count= process_and_count_tables(tables_used, tables_type))) DBUG_RETURN(0); @@ -3062,10 +3073,10 @@ Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex, } DBUG_PRINT("qcache", - ("not interesting query: %d or not cacheable, options %lx %lx, type %u", + ("not interesting query: %d or not cacheable, options %lx %lx type: %u", (int) lex->sql_command, - OPTION_TO_QUERY_CACHE, - lex->select_lex.options, + (long) OPTION_TO_QUERY_CACHE, + (long) lex->select_lex.options, (int) thd->variables.query_cache_type)); DBUG_RETURN(0); } @@ -3530,7 +3541,7 @@ uint Query_cache::filename_2_table_key (char *key, const char *path, #if defined(DBUG_OFF) && !defined(USE_QUERY_CACHE_INTEGRITY_CHECK) -void wreck(uint line, const char *message) {} +void wreck(uint line, const char *message) { query_cache_size = 0; } void bins_dump() {} void cache_dump() {} void queries_dump() {} @@ -3542,6 +3553,17 @@ my_bool in_blocks(Query_cache_block * point) { return 0; } #else + +/* + Debug method which switch query cache off but left content for + investigation. + + SYNOPSIS + Query_cache::wreck() + line line of the wreck() call + message message for logging +*/ + void Query_cache::wreck(uint line, const char *message) { THD *thd=current_thd; @@ -3656,7 +3678,8 @@ void Query_cache::queries_dump() DBUG_PRINT("qcache", ("F:%u C:%u L:%lu T:'%s' (%u) '%s' '%s'", flags.client_long_flag, flags.character_set_client_num, - (ulong)flags.limit, flags.time_zone->get_name(), + (ulong)flags.limit, + flags.time_zone->get_name()->ptr(), len, str, strend(str)+1)); DBUG_PRINT("qcache", ("-b- 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx", (ulong) block, (ulong) block->next, (ulong) block->prev, @@ -3765,7 +3788,7 @@ my_bool Query_cache::check_integrity(bool locked) { DBUG_PRINT("error", ("block 0x%lx do not aligned by %d", (ulong) block, - ALIGN_SIZE(1))); + (int) ALIGN_SIZE(1))); result = 1; } // Check memory allocation @@ -3876,9 +3899,8 @@ my_bool Query_cache::check_integrity(bool locked) break; } default: - DBUG_PRINT("error", - ("block 0x%lx have incorrect type %u", - block, block->type)); + DBUG_PRINT("error", ("block 0x%lx have incorrect type %u", + (long) block, block->type)); result = 1; } @@ -3976,8 +3998,8 @@ my_bool Query_cache::check_integrity(bool locked) } while (block != bins[i].free_blocks); if (count != bins[i].number) { - DBUG_PRINT("error", ("bin[%d].number is %d, but bin have %d blocks", - bins[i].number, count)); + DBUG_PRINT("error", ("bins[%d].number = %d, but bin have %d blocks", + i, bins[i].number, count)); result = 1; } } diff --git a/sql/sql_cache.h b/sql/sql_cache.h index a66067159e2..bc00f7ea629 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2001-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -128,7 +127,7 @@ struct Query_cache_query inline void tables_type(uint8 type) { tbls_type= type; } inline ulong length() { return len; } inline ulong add(ulong packet_len) { return(len+= packet_len); } - inline void length(ulong length) { len= length; } + inline void length(ulong length_arg) { len= length_arg; } inline gptr query() { return (gptr)(((byte*)this)+ @@ -156,7 +155,7 @@ struct Query_cache_table inline char *db() { return (char *) data(); } inline char *table() { return tbl; } - inline void table(char *table) { tbl= table; } + inline void table(char *table_arg) { tbl= table_arg; } inline uint32 key_length() { return key_len; } inline void key_length(uint32 len) { key_len= len; } inline uint8 type() { return table_type; } @@ -164,7 +163,7 @@ struct Query_cache_table inline qc_engine_callback callback() { return callback_func; } inline void callback(qc_engine_callback fn){ callback_func= fn; } inline ulonglong engine_data() { return engine_data_buff; } - inline void engine_data(ulonglong data) { engine_data_buff= data; } + inline void engine_data(ulonglong data_arg){ engine_data_buff= data_arg; } inline gptr data() { return (gptr)(((byte*)this)+ diff --git a/sql/sql_class.cc b/sql/sql_class.cc index e96c96cd869..96a84d2fd46 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -193,20 +192,14 @@ void **thd_ha_data(const THD *thd, const struct handlerton *hton) } -/* - Pass nominal parameters to Statement constructor only to ensure that - the destructor works OK in case of error. The main_mem_root will be - re-initialized in init(). -*/ THD::THD() - :Statement(CONVENTIONAL_EXECUTION, 0, ALLOC_ROOT_MIN_BLOCK_SIZE, 0), + :Statement(&main_lex, &main_mem_root, CONVENTIONAL_EXECUTION, + /* statement id */ 0), Open_tables_state(refresh_version), rli_fake(0), lock_id(&main_lock_id), user_time(0), in_sub_stmt(0), -#ifdef HAVE_ROW_BASED_REPLICATION binlog_table_maps(0), -#endif /*HAVE_ROW_BASED_REPLICATION*/ global_read_lock(0), is_fatal_error(0), rand_used(0), time_zone_used(0), arg_of_last_insert_id_function(FALSE), @@ -217,6 +210,14 @@ THD::THD() stmt_depends_on_first_successful_insert_id_in_prev_stmt(FALSE), spcont(NULL) { + ulong tmp; + + /* + Pass nominal parameters to init_alloc_root only to ensure that + the destructor works OK in case of an error. The main_mem_root + will be re-initialized in init_for_queries(). + */ + init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0); stmt_arena= this; thread_stack= 0; db= 0; @@ -241,9 +242,10 @@ THD::THD() // Must be reset to handle error with THD's created for init of mysqld lex->current_select= 0; start_time=(time_t) 0; + time_after_lock=(time_t) 0; current_linfo = 0; slave_thread = 0; - variables.pseudo_thread_id= 0; + thread_id= variables.pseudo_thread_id= 0; one_shot_set= 0; file_id = 0; query_id= 0; @@ -261,17 +263,14 @@ THD::THD() #endif client_capabilities= 0; // minimalistic client net.last_error[0]=0; // If error on boot +#ifdef HAVE_QUERY_CACHE query_cache_init_query(&net); // If error on boot +#endif ull=0; system_thread= NON_SYSTEM_THREAD; cleanup_done= abort_on_warning= no_warnings_for_error= 0; peer_port= 0; // For SHOW PROCESSLIST -#ifdef HAVE_ROW_BASED_REPLICATION transaction.m_pending_rows_event= 0; -#endif -#ifdef __WIN__ - real_id = 0; -#endif #ifdef SIGNAL_WITH_VIO_CLOSE active_vio = 0; #endif @@ -309,11 +308,43 @@ THD::THD() protocol_prep.init(this); tablespace_op=FALSE; - ulong tmp=sql_rnd_with_mutex(); - randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::query_id); + tmp= sql_rnd_with_mutex(); + randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::global_query_id); substitute_null_with_insert_id = FALSE; thr_lock_info_init(&lock_info); /* safety: will be reset after start */ thr_lock_owner_init(&main_lock_id, &lock_info); + + m_internal_handler= NULL; +} + + +void THD::push_internal_handler(Internal_error_handler *handler) +{ + /* + TODO: The current implementation is limited to 1 handler at a time only. + THD and sp_rcontext need to be modified to use a common handler stack. + */ + DBUG_ASSERT(m_internal_handler == NULL); + m_internal_handler= handler; +} + + +bool THD::handle_error(uint sql_errno, + MYSQL_ERROR::enum_warning_level level) +{ + if (m_internal_handler) + { + return m_internal_handler->handle_error(sql_errno, level, this); + } + + return FALSE; // 'FALSE', as per coding style +} + + +void THD::pop_internal_handler() +{ + DBUG_ASSERT(m_internal_handler != NULL); + m_internal_handler= NULL; } @@ -331,9 +362,6 @@ void THD::init(void) variables.date_format); variables.datetime_format= date_time_format_copy((THD*) 0, variables.datetime_format); -#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE - variables.ndb_use_transactions= 1; -#endif pthread_mutex_unlock(&LOCK_global_system_variables); server_status= SERVER_STATUS_AUTOCOMMIT; if (variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) @@ -348,9 +376,7 @@ void THD::init(void) bzero((char*) warn_count, sizeof(warn_count)); total_warn_count= 0; update_charset(); -#ifdef HAVE_ROW_BASED_REPLICATION reset_current_stmt_binlog_row_based(); -#endif /*HAVE_ROW_BASED_REPLICATION*/ bzero((char *) &status_var, sizeof(status_var)); variables.lc_time_names = &my_locale_en_US; } @@ -364,6 +390,7 @@ void THD::init(void) void THD::init_for_queries() { + set_time(); ha_enable_transaction(this,TRUE); reset_root_defaults(mem_root, variables.query_alloc_block_size, @@ -408,6 +435,8 @@ void THD::change_user(void) void THD::cleanup(void) { DBUG_ENTER("THD::cleanup"); + DBUG_ASSERT(cleanup_done == 0); + #ifdef ENABLE_WHEN_BINLOG_WILL_BE_ABLE_TO_PREPARE if (transaction.xid_state.xa_state == XA_PREPARED) { @@ -443,7 +472,6 @@ void THD::cleanup(void) pthread_mutex_lock(&LOCK_user_locks); item_user_lock_release(ull); pthread_mutex_unlock(&LOCK_user_locks); - ull= 0; } cleanup_done=1; @@ -492,6 +520,7 @@ THD::~THD() delete rli_fake; #endif + free_root(&main_mem_root, MYF(0)); DBUG_VOID_RETURN; } @@ -512,14 +541,13 @@ THD::~THD() void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var) { - ulong *end= (ulong*) ((byte*) to_var + offsetof(STATUS_VAR, - last_system_status_var) + + ulong *end= (ulong*) ((byte*) to_var + + offsetof(STATUS_VAR, last_system_status_var) + sizeof(ulong)); ulong *to= (ulong*) to_var, *from= (ulong*) from_var; while (to != end) *(to++)+= *(from++); - /* it doesn't make sense to add last_query_cost values */ } /* @@ -551,14 +579,16 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var, void THD::awake(THD::killed_state state_to_set) { DBUG_ENTER("THD::awake"); - DBUG_PRINT("enter", ("this=0x%lx", this)); + DBUG_PRINT("enter", ("this: 0x%lx", (long) this)); THD_CHECK_SENTRY(this); safe_mutex_assert_owner(&LOCK_delete); killed= state_to_set; if (state_to_set != THD::KILL_QUERY) { - thr_alarm_kill(real_id); + thr_alarm_kill(thread_id); + if (!slave_thread) + thread_scheduler.post_kill_notification(this); #ifdef SIGNAL_WITH_VIO_CLOSE close_active_vio(); #endif @@ -609,18 +639,19 @@ bool THD::store_globals() Assert that thread_stack is initialized: it's necessary to be able to track stack overrun. */ - DBUG_ASSERT(this->thread_stack); + DBUG_ASSERT(thread_stack); if (my_pthread_setspecific_ptr(THR_THD, this) || my_pthread_setspecific_ptr(THR_MALLOC, &mem_root)) return 1; mysys_var=my_thread_var; - dbug_thread_id=my_thread_id(); /* - By default 'slave_proxy_id' is 'thread_id'. They may later become different - if this is the slave SQL thread. + Let mysqld define the thread id (not mysys) + This allows us to move THD to different threads if needed. */ - variables.pseudo_thread_id= thread_id; + mysys_var->id= thread_id; + real_id= pthread_self(); // For debugging + /* We have to call thr_lock_info_init() again here as THD may have been created in another thread @@ -799,7 +830,7 @@ void THD::add_changed_table(const char *key, long key_length) { list_include(prev_changed, curr, changed_table_dup(key, key_length)); DBUG_PRINT("info", - ("key_length %u %u", key_length, (*prev_changed)->key_length)); + ("key_length %ld %u", key_length, (*prev_changed)->key_length)); DBUG_VOID_RETURN; } else if (cmp == 0) @@ -809,7 +840,7 @@ void THD::add_changed_table(const char *key, long key_length) { list_include(prev_changed, curr, changed_table_dup(key, key_length)); DBUG_PRINT("info", - ("key_length %u %u", key_length, + ("key_length %ld %u", key_length, (*prev_changed)->key_length)); DBUG_VOID_RETURN; } @@ -821,7 +852,7 @@ void THD::add_changed_table(const char *key, long key_length) } } *prev_changed = changed_table_dup(key, key_length); - DBUG_PRINT("info", ("key_length %u %u", key_length, + DBUG_PRINT("info", ("key_length %ld %u", key_length, (*prev_changed)->key_length)); DBUG_VOID_RETURN; } @@ -858,7 +889,6 @@ int THD::send_explain_fields(select_result *result) field_list.push_back(new Item_empty_string("select_type", 19, cs)); field_list.push_back(item= new Item_empty_string("table", NAME_LEN, cs)); item->maybe_null= 1; -#ifdef WITH_PARTITION_STORAGE_ENGINE if (lex->describe & DESCRIBE_PARTITIONS) { /* Maximum length of string that make_used_partitions_str() can produce */ @@ -867,7 +897,6 @@ int THD::send_explain_fields(select_result *result) field_list.push_back(item); item->maybe_null= 1; } -#endif field_list.push_back(item= new Item_empty_string("type", 10, cs)); item->maybe_null= 1; field_list.push_back(item=new Item_empty_string("possible_keys", @@ -987,6 +1016,13 @@ void select_result::cleanup() /* do nothing */ } +bool select_result::check_simple_select() const +{ + my_error(ER_SP_BAD_CURSOR_QUERY, MYF(0)); + return TRUE; +} + + static String default_line_term("\n",default_charset_info); static String default_escaped("\\",default_charset_info); static String default_field_term("\t",default_charset_info); @@ -998,6 +1034,7 @@ sql_exchange::sql_exchange(char *name,bool flag) enclosed= line_start= &my_empty_string; line_term= &default_line_term; escaped= &default_escaped; + cs= NULL; } bool select_send::send_fields(List<Item> &list, uint flags) @@ -1182,7 +1219,7 @@ static File create_file(THD *thd, char *path, sql_exchange *exchange, IO_CACHE *cache) { File file; - uint option= MY_UNPACK_FILENAME; + uint option= MY_UNPACK_FILENAME | MY_RELATIVE_PATH; #ifdef DONT_ALLOW_FULL_LOAD_DATA_PATHS option|= MY_REPLACE_DIR; // Force use of db directory @@ -1196,7 +1233,15 @@ static File create_file(THD *thd, char *path, sql_exchange *exchange, } else (void) fn_format(path, exchange->file_name, mysql_real_data_home, "", option); - + + if (opt_secure_file_priv && + strncmp(opt_secure_file_priv, path, strlen(opt_secure_file_priv))) + { + /* Write only allowed to dir or subdir specified by secure_file_priv */ + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--secure-file-priv"); + return -1; + } + if (!access(path, F_OK)) { my_error(ER_FILE_EXISTS_ERROR, MYF(0), exchange->file_name); @@ -1537,7 +1582,7 @@ bool select_max_min_finder_subselect::send_data(List<Item> &items) bool select_max_min_finder_subselect::cmp_real() { - Item *maxmin= ((Item_singlerow_subselect *)item)->el(0); + Item *maxmin= ((Item_singlerow_subselect *)item)->element_index(0); double val1= cache->val_real(), val2= maxmin->val_real(); if (fmax) return (cache->null_value && !maxmin->null_value) || @@ -1550,7 +1595,7 @@ bool select_max_min_finder_subselect::cmp_real() bool select_max_min_finder_subselect::cmp_int() { - Item *maxmin= ((Item_singlerow_subselect *)item)->el(0); + Item *maxmin= ((Item_singlerow_subselect *)item)->element_index(0); longlong val1= cache->val_int(), val2= maxmin->val_int(); if (fmax) return (cache->null_value && !maxmin->null_value) || @@ -1563,7 +1608,7 @@ bool select_max_min_finder_subselect::cmp_int() bool select_max_min_finder_subselect::cmp_decimal() { - Item *maxmin= ((Item_singlerow_subselect *)item)->el(0); + Item *maxmin= ((Item_singlerow_subselect *)item)->element_index(0); my_decimal cval, *cvalue= cache->val_decimal(&cval); my_decimal mval, *mvalue= maxmin->val_decimal(&mval); if (fmax) @@ -1578,7 +1623,7 @@ bool select_max_min_finder_subselect::cmp_decimal() bool select_max_min_finder_subselect::cmp_str() { String *val1, *val2, buf1, buf2; - Item *maxmin= ((Item_singlerow_subselect *)item)->el(0); + Item *maxmin= ((Item_singlerow_subselect *)item)->element_index(0); /* as far as both operand is Item_cache buf1 & buf2 will not be used, but added for safety @@ -1615,53 +1660,28 @@ bool select_exists_subselect::send_data(List<Item> &items) int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u) { - List_iterator_fast<Item> li(list); - List_iterator_fast<my_var> gl(var_list); - Item *item; - - local_vars.empty(); // Clear list if SP unit= u; - row_count= 0; - + if (var_list.elements != list.elements) { my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, ER(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT), MYF(0)); return 1; - } - while ((item=li++)) - { - my_var *mv= gl++; - if (mv->local) - { - Item_splocal *var= new Item_splocal(mv->s, mv->offset, mv->type); - (void)local_vars.push_back(var); -#ifndef DBUG_OFF - var->m_sp= mv->sp; -#endif - } - else - { - Item_func_set_user_var *var= new Item_func_set_user_var(mv->s, item); - /* - Item_func_set_user_var can't substitute something else on its place => - 0 can be passed as last argument (reference on item) - Item_func_set_user_var can't be fixed after creation, so we do not - check var->fixed - */ - var->fix_fields(thd, 0); - var->fix_length_and_dec(); - vars.push_back(var); - } - } + } return 0; } +bool select_dumpvar::check_simple_select() const +{ + my_error(ER_SP_BAD_CURSOR_SELECT, MYF(0)); + return TRUE; +} + + void select_dumpvar::cleanup() { - vars.empty(); - row_count=0; + row_count= 0; } @@ -1704,18 +1724,17 @@ void Query_arena::cleanup_stmt() Statement functions */ -Statement::Statement(enum enum_state state_arg, ulong id_arg, - ulong alloc_block_size, ulong prealloc_size) - :Query_arena(&main_mem_root, state_arg), +Statement::Statement(LEX *lex_arg, MEM_ROOT *mem_root_arg, + enum enum_state state_arg, ulong id_arg) + :Query_arena(mem_root_arg, state_arg), id(id_arg), mark_used_columns(MARK_COLUMNS_READ), - lex(&main_lex), + lex(lex_arg), query(0), query_length(0), cursor(0) { name.str= NULL; - init_sql_alloc(&main_mem_root, alloc_block_size, prealloc_size); } @@ -1757,7 +1776,7 @@ void Statement::restore_backup_statement(Statement *stmt, Statement *backup) void THD::end_statement() { - /* Cleanup SQL processing state to resuse this statement in next query. */ + /* Cleanup SQL processing state to reuse this statement in next query. */ lex_end(lex); delete lex->result; lex->result= 0; @@ -1798,12 +1817,6 @@ void THD::restore_active_arena(Query_arena *set, Query_arena *backup) Statement::~Statement() { - /* - We must free `main_mem_root', not `mem_root' (pointer), to work - correctly if this statement is used as a backup statement, - for which `mem_root' may point to some other statement. - */ - free_root(&main_mem_root, MYF(0)); } C_MODE_START @@ -1970,13 +1983,10 @@ Statement_map::~Statement_map() bool select_dumpvar::send_data(List<Item> &items) { - List_iterator_fast<Item_func_set_user_var> li(vars); - List_iterator_fast<Item_splocal> var_li(local_vars); - List_iterator_fast<my_var> my_li(var_list); + List_iterator_fast<my_var> var_li(var_list); List_iterator<Item> it(items); - Item_func_set_user_var *xx; - Item_splocal *yy; - my_var *zz; + Item *item; + my_var *mv; DBUG_ENTER("select_dumpvar::send_data"); if (unit->offset_limit_cnt) @@ -1989,24 +1999,19 @@ bool select_dumpvar::send_data(List<Item> &items) my_message(ER_TOO_MANY_ROWS, ER(ER_TOO_MANY_ROWS), MYF(0)); DBUG_RETURN(1); } - while ((zz=my_li++) && (it++)) + while ((mv= var_li++) && (item= it++)) { - if (zz->local) + if (mv->local) { - if ((yy=var_li++)) - { - if (thd->spcont->set_variable(current_thd, yy->get_var_idx(), - it.ref())) - DBUG_RETURN(1); - } + if (thd->spcont->set_variable(thd, mv->offset, &item)) + DBUG_RETURN(1); } else { - if ((xx=li++)) - { - xx->check(0); - xx->update(); - } + Item_func_set_user_var *suv= new Item_func_set_user_var(mv->s, item); + suv->fix_fields(thd, 0); + suv->check(0); + suv->update(); } } DBUG_RETURN(0); @@ -2192,7 +2197,12 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, !current_stmt_binlog_row_based) { options&= ~OPTION_BIN_LOG; - } + } + + if ((backup->options & OPTION_BIN_LOG) && is_update_query(lex->sql_command)&& + !current_stmt_binlog_row_based) + mysql_bin_log.start_union_events(this, this->query_id); + /* Disable result sets */ client_capabilities &= ~CLIENT_MULTI_RESULTS; in_sub_stmt|= new_state; @@ -2236,6 +2246,10 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) sent_row_count= backup->sent_row_count; client_capabilities= backup->client_capabilities; + if ((options & OPTION_BIN_LOG) && is_update_query(lex->sql_command) && + !current_stmt_binlog_row_based) + mysql_bin_log.stop_union_events(this); + /* The following is added to the old values as we are interested in the total complexity of the query @@ -2336,7 +2350,6 @@ void xid_cache_delete(XID_STATE *xid_state) */ #ifndef MYSQL_CLIENT -#ifdef HAVE_ROW_BASED_REPLICATION /* Template member function for ensuring that there is an rows log @@ -2446,11 +2459,12 @@ THD::binlog_prepare_pending_rows_event(TABLE*, uint32, MY_BITMAP const*, my_size_t colcnt, my_size_t, bool, Update_rows_log_event *); #endif + +#ifdef NOT_USED static char const* field_type_name(enum_field_types type) { - switch (type) - { + switch (type) { case MYSQL_TYPE_DECIMAL: return "MYSQL_TYPE_DECIMAL"; case MYSQL_TYPE_TINY: @@ -2508,6 +2522,7 @@ field_type_name(enum_field_types type) } return "Unknown"; } +#endif my_size_t THD::max_row_length_blob(TABLE *table, const byte *data) const @@ -2520,7 +2535,9 @@ my_size_t THD::max_row_length_blob(TABLE *table, const byte *data) const for (uint *ptr= beg ; ptr != end ; ++ptr) { Field_blob* const blob= (Field_blob*) table->field[*ptr]; - length+= blob->get_length((const char *) (data + blob->offset())) + 2; + length+= blob->get_length((const char*) (data + + blob->offset(table->record[0]))) + + HA_KEY_BLOB_LENGTH; } return length; @@ -2544,7 +2561,7 @@ my_size_t THD::pack_row(TABLE *table, MY_BITMAP const* cols, byte *row_data, if (bitmap_is_set(cols,i)) { my_ptrdiff_t const offset= - field->is_null(rec_offset) ? def_offset : rec_offset; + field->is_null((uint) rec_offset) ? def_offset : rec_offset; field->move_field_offset(offset); ptr= (byte*)field->pack((char *) ptr, field->ptr); field->move_field_offset(-offset); @@ -2554,6 +2571,128 @@ my_size_t THD::pack_row(TABLE *table, MY_BITMAP const* cols, byte *row_data, } +namespace { + /** + Class to handle temporary allocation of memory for row data. + + The responsibilities of the class is to provide memory for + packing one or two rows of packed data (depending on what + constructor is called). + + In order to make the allocation more efficient for "simple" rows, + i.e., rows that do not contain any blobs, a pointer to the + allocated memory is of memory is stored in the table structure + for simple rows. If memory for a table containing a blob field + is requested, only memory for that is allocated, and subsequently + released when the object is destroyed. + + */ + class Row_data_memory { + public: + /** + Build an object to keep track of a block-local piece of memory + for storing a row of data. + + @param table + Table where the pre-allocated memory is stored. + + @param length + Length of data that is needed, if the record contain blobs. + */ + Row_data_memory(TABLE *table, my_size_t const len1) + : m_memory(0) + { +#ifndef DBUG_OFF + m_alloc_checked= FALSE; +#endif + allocate_memory(table, len1); + m_ptr[0]= has_memory() ? m_memory : 0; + m_ptr[1]= 0; + } + + Row_data_memory(TABLE *table, my_size_t const len1, my_size_t const len2) + : m_memory(0) + { +#ifndef DBUG_OFF + m_alloc_checked= FALSE; +#endif + allocate_memory(table, len1 + len2); + m_ptr[0]= has_memory() ? m_memory : 0; + m_ptr[1]= has_memory() ? m_memory + len1 : 0; + } + + ~Row_data_memory() + { + if (m_memory != 0 && m_release_memory_on_destruction) + my_free((gptr) m_memory, MYF(MY_WME)); + } + + /** + Is there memory allocated? + + @retval true There is memory allocated + @retval false Memory allocation failed + */ + bool has_memory() const { +#ifndef DBUG_OFF + m_alloc_checked= TRUE; +#endif + return m_memory != 0; + } + + byte *slot(uint s) + { + DBUG_ASSERT(s < sizeof(m_ptr)/sizeof(*m_ptr)); + DBUG_ASSERT(m_ptr[s] != 0); + DBUG_ASSERT(m_alloc_checked == TRUE); + return m_ptr[s]; + } + + private: + void allocate_memory(TABLE *const table, my_size_t const total_length) + { + if (table->s->blob_fields == 0) + { + /* + The maximum length of a packed record is less than this + length. We use this value instead of the supplied length + when allocating memory for records, since we don't know how + the memory will be used in future allocations. + + Since table->s->reclength is for unpacked records, we have + to add two bytes for each field, which can potentially be + added to hold the length of a packed field. + */ + my_size_t const maxlen= table->s->reclength + 2 * table->s->fields; + + /* + Allocate memory for two records if memory hasn't been + allocated. We allocate memory for two records so that it can + be used when processing update rows as well. + */ + if (table->write_row_record == 0) + table->write_row_record= + (byte *) alloc_root(&table->mem_root, 2 * maxlen); + m_memory= table->write_row_record; + m_release_memory_on_destruction= FALSE; + } + else + { + m_memory= (byte *) my_malloc(total_length, MYF(MY_WME)); + m_release_memory_on_destruction= TRUE; + } + } + +#ifndef DBUG_OFF + mutable bool m_alloc_checked; +#endif + bool m_release_memory_on_destruction; + byte *m_memory; + byte *m_ptr[2]; + }; +} + + int THD::binlog_write_row(TABLE* table, bool is_trans, MY_BITMAP const* cols, my_size_t colcnt, byte const *record) @@ -2564,40 +2703,23 @@ int THD::binlog_write_row(TABLE* table, bool is_trans, Pack records into format for transfer. We are allocating more memory than needed, but that doesn't matter. */ - bool error= 0; - byte *row_data= table->write_row_record; - my_size_t const max_len= max_row_length(table, record); - my_size_t len; - Rows_log_event *ev; - - /* Allocate room for a row (if needed) */ - if (!row_data) - { - if (!table->s->blob_fields) - { - /* multiply max_len by 2 so it can be used for update_row as well */ - table->write_row_record= (byte *) alloc_root(&table->mem_root, - 2*max_len); - if (!table->write_row_record) - return HA_ERR_OUT_OF_MEM; - row_data= table->write_row_record; - } - else if (unlikely(!(row_data= (byte *) my_malloc(max_len, MYF(MY_WME))))) - return HA_ERR_OUT_OF_MEM; - } - len= pack_row(table, cols, row_data, record); + Row_data_memory memory(table, max_row_length(table, record)); + if (!memory.has_memory()) + return HA_ERR_OUT_OF_MEM; - ev= binlog_prepare_pending_rows_event(table, server_id, cols, colcnt, - len, is_trans, - static_cast<Write_rows_log_event*>(0)); + byte *row_data= memory.slot(0); - /* add_row_data copies row_data to internal buffer */ - error= likely(ev != 0) ? ev->add_row_data(row_data,len) : HA_ERR_OUT_OF_MEM ; + my_size_t const len= pack_row(table, cols, row_data, record); - if (table->write_row_record == 0) - my_free((gptr) row_data, MYF(MY_WME)); + Rows_log_event* const ev= + binlog_prepare_pending_rows_event(table, server_id, cols, colcnt, + len, is_trans, + static_cast<Write_rows_log_event*>(0)); - return error; + if (unlikely(ev == 0)) + return HA_ERR_OUT_OF_MEM; + + return ev->add_row_data(row_data, len); } int THD::binlog_update_row(TABLE* table, bool is_trans, @@ -2607,53 +2729,43 @@ int THD::binlog_update_row(TABLE* table, bool is_trans, { DBUG_ASSERT(current_stmt_binlog_row_based && mysql_bin_log.is_open()); - bool error= 0; my_size_t const before_maxlen = max_row_length(table, before_record); my_size_t const after_maxlen = max_row_length(table, after_record); - byte *row_data= table->write_row_record; - byte *before_row, *after_row; - if (row_data != 0) - { - before_row= row_data; - after_row= before_row + before_maxlen; - } - else - { - if (unlikely(!(row_data= (byte*)my_multi_malloc(MYF(MY_WME), - &before_row, before_maxlen, - &after_row, after_maxlen, - NULL)))) - return HA_ERR_OUT_OF_MEM; - } + Row_data_memory row_data(table, before_maxlen, after_maxlen); + if (!row_data.has_memory()) + return HA_ERR_OUT_OF_MEM; + + byte *before_row= row_data.slot(0); + byte *after_row= row_data.slot(1); my_size_t const before_size= pack_row(table, cols, before_row, before_record); my_size_t const after_size= pack_row(table, cols, after_row, after_record); - - DBUG_DUMP("before_record", (char *)before_record, table->s->reclength); - DBUG_DUMP("after_record", (char *)after_record, table->s->reclength); - DBUG_DUMP("before_row", (char *)before_row, before_size); - DBUG_DUMP("after_row", (char *)after_row, after_size); + + /* + Don't print debug messages when running valgrind since they can + trigger false warnings. + */ +#ifndef HAVE_purify + DBUG_DUMP("before_record", (const char *)before_record, table->s->reclength); + DBUG_DUMP("after_record", (const char *)after_record, table->s->reclength); + DBUG_DUMP("before_row", (const char *)before_row, before_size); + DBUG_DUMP("after_row", (const char *)after_row, after_size); +#endif Rows_log_event* const ev= binlog_prepare_pending_rows_event(table, server_id, cols, colcnt, before_size + after_size, is_trans, static_cast<Update_rows_log_event*>(0)); - error= - unlikely(!ev) || + if (unlikely(ev == 0)) + return HA_ERR_OUT_OF_MEM; + + return ev->add_row_data(before_row, before_size) || ev->add_row_data(after_row, after_size); - - if (!table->write_row_record) - { - /* add_row_data copies row_data to internal buffer */ - my_free((gptr)row_data, MYF(MY_WME)); - } - - return error; } int THD::binlog_delete_row(TABLE* table, bool is_trans, @@ -2666,11 +2778,12 @@ int THD::binlog_delete_row(TABLE* table, bool is_trans, Pack records into format for transfer. We are allocating more memory than needed, but that doesn't matter. */ - bool error= 0; - my_size_t const max_len= max_row_length(table, record); - byte *row_data= table->write_row_record; - if (!row_data && unlikely(!(row_data= (byte*)my_malloc(max_len, MYF(MY_WME))))) + Row_data_memory memory(table, max_row_length(table, record)); + if (unlikely(!memory.has_memory())) return HA_ERR_OUT_OF_MEM; + + byte *row_data= memory.slot(0); + my_size_t const len= pack_row(table, cols, row_data, record); Rows_log_event* const ev= @@ -2678,13 +2791,10 @@ int THD::binlog_delete_row(TABLE* table, bool is_trans, len, is_trans, static_cast<Delete_rows_log_event*>(0)); - error= (unlikely(!ev)) || ev->add_row_data(row_data, len); - - /* add_row_data copies row_data */ - if (table->write_row_record == 0) - my_free((gptr)row_data, MYF(MY_WME)); + if (unlikely(ev == 0)) + return HA_ERR_OUT_OF_MEM; - return error; + return ev->add_row_data(row_data, len); } @@ -2729,8 +2839,6 @@ void THD::binlog_delete_pending_rows_event() } } -#endif /* HAVE_ROW_BASED_REPLICATION */ - /* Member function that will log query, either row-based or statement-based depending on the value of the 'current_stmt_binlog_row_based' @@ -2771,18 +2879,14 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, If we are in prelocked mode, the flushing will be done inside the top-most close_thread_tables(). */ -#ifdef HAVE_ROW_BASED_REPLICATION if (this->prelocked_mode == NON_PRELOCKED) if (int error= binlog_flush_pending_rows_event(TRUE)) DBUG_RETURN(error); -#endif /*HAVE_ROW_BASED_REPLICATION*/ switch (qtype) { case THD::ROW_QUERY_TYPE: -#ifdef HAVE_ROW_BASED_REPLICATION if (current_stmt_binlog_row_based) DBUG_RETURN(0); -#endif /* Otherwise, we fall through */ case THD::MYSQL_QUERY_TYPE: /* @@ -2800,9 +2904,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, */ { Query_log_event qinfo(this, query, query_len, is_trans, suppress_use); -#ifdef HAVE_ROW_BASED_REPLICATION qinfo.flags|= LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F; -#endif /* Binlog table maps will be irrelevant after a Query_log_event (they are just removed on the slave side) so after the query @@ -2810,9 +2912,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, table maps were written. */ int error= mysql_bin_log.write(&qinfo); -#ifdef HAVE_ROW_BASED_REPLICATION binlog_table_maps= 0; -#endif /*HAVE_ROW_BASED_REPLICATION*/ DBUG_RETURN(error); } break; diff --git a/sql/sql_class.h b/sql/sql_class.h index 95283ec2fc8..ffc8b2c46d1 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -183,6 +182,8 @@ struct system_variables { ulonglong myisam_max_extra_sort_file_size; ulonglong myisam_max_sort_file_size; + ulonglong max_heap_table_size; + ulonglong tmp_table_size; ha_rows select_limit; ha_rows max_join_size; ulong auto_increment_increment, auto_increment_offset; @@ -191,7 +192,6 @@ struct system_variables ulong long_query_time; ulong max_allowed_packet; ulong max_error_count; - ulong max_heap_table_size; ulong max_length_for_sort_data; ulong max_sort_length; ulong max_tmp_tables; @@ -214,8 +214,7 @@ struct system_variables ulong read_rnd_buff_size; ulong div_precincrement; ulong sortbuff_size; - handlerton *table_type; - ulong tmp_table_size; + ulong thread_handling; ulong tx_isolation; ulong completion_type; /* Determines which non-standard SQL behaviour should be enabled */ @@ -232,11 +231,15 @@ struct system_variables ulong trans_prealloc_size; ulong log_warnings; ulong group_concat_max_len; + ulong ndb_autoincrement_prefetch_sz; + ulong ndb_index_stat_cache_entries; + ulong ndb_index_stat_update_freq; + ulong binlog_format; // binlog format for this thd (see enum_binlog_format) /* In slave thread we need to know in behalf of which thread the query is being run to replicate temp tables properly */ - ulong pseudo_thread_id; + my_thread_id pseudo_thread_id; my_bool low_priority_updates; my_bool new_mode; @@ -249,14 +252,12 @@ struct system_variables my_bool ndb_use_exact_count; my_bool ndb_use_transactions; my_bool ndb_index_stat_enable; - ulong ndb_autoincrement_prefetch_sz; - ulong ndb_index_stat_cache_entries; - ulong ndb_index_stat_update_freq; - ulong binlog_format; // binlog format for this thd (see enum_binlog_format) my_bool old_alter_table; my_bool old_passwords; + handlerton *table_type; + /* Only charset part of these variables is sensible */ CHARSET_INFO *character_set_filesystem; CHARSET_INFO *character_set_client; @@ -337,12 +338,17 @@ typedef struct system_status_var ulong com_stmt_reset; ulong com_stmt_close; + /* + Status variables which it does not make sense to add to + global status variable counter + */ double last_query_cost; } STATUS_VAR; /* - This is used for 'show status'. It must be updated to the last ulong - variable in system_status_var + This is used for 'SHOW STATUS'. It must be updated to the last ulong + variable in system_status_var which is makes sens to add to the global + counter */ #define last_system_status_var com_stmt_close @@ -420,6 +426,12 @@ public: { return strdup_root(mem_root,str); } inline char *strmake(const char *str, uint size) { return strmake_root(mem_root,str,size); } + inline bool LEX_STRING_make(LEX_STRING *lex_str, const char *str, uint size) + { + return ((lex_str->str= + strmake_root(mem_root, str, (lex_str->length= size)))) == 0; + } + inline char *memdup(const char *str, uint size) { return memdup_root(mem_root,str,size); } inline char *memdup_w_gap(const char *str, uint size, uint gap) @@ -440,8 +452,10 @@ public: class Server_side_cursor; -/* - State of a single command executed against this connection. +/** + @class Statement + @brief State of a single command executed against this connection. + One connection can contain a lot of simultaneously running statements, some of which could be: - prepared, that is, contain placeholders, @@ -459,10 +473,6 @@ class Statement: public ilink, public Query_arena Statement(const Statement &rhs); /* not implemented: */ Statement &operator=(const Statement &rhs); /* non-copyable */ public: - /* FIXME: these must be protected */ - MEM_ROOT main_mem_root; - LEX main_lex; - /* Uniquely identifies each statement object in thread scope; change during statement lifetime. FIXME: must be const @@ -513,10 +523,10 @@ public: public: /* This constructor is called for backup statements */ - Statement() { clear_alloc_root(&main_mem_root); } + Statement() {} - Statement(enum enum_state state_arg, ulong id_arg, - ulong alloc_block_size, ulong prealloc_size); + Statement(LEX *lex_arg, MEM_ROOT *mem_root_arg, + enum enum_state state_arg, ulong id_arg); virtual ~Statement(); /* Assign execution context (note: not all members) of given stmt to self */ @@ -528,7 +538,7 @@ public: }; -/* +/** Container for all statements created/used in a connection. Statements in Statement_map have unique Statement::id (guaranteed by id assignment in Statement::Statement) @@ -608,6 +618,10 @@ bool xid_cache_insert(XID *xid, enum xa_states xa_state); bool xid_cache_insert(XID_STATE *xid_state); void xid_cache_delete(XID_STATE *xid_state); +/** + @class Security_context + @brief A set of THD members describing the current authenticated user. +*/ class Security_context { public: @@ -639,7 +653,7 @@ public: }; -/* +/** A registry for item tree transformations performed during query optimization. We register only those changes which require a rollback to re-execute a prepared statement or stored procedure @@ -650,7 +664,7 @@ struct Item_change_record; typedef I_List<Item_change_record> Item_change_list; -/* +/** Type of prelocked mode. See comment for THD::prelocked_mode for complete description. */ @@ -659,7 +673,7 @@ enum prelocked_mode_type {NON_PRELOCKED= 0, PRELOCKED= 1, PRELOCKED_UNDER_LOCK_TABLES= 2}; -/* +/** Class that holds information about tables which were opened and locked by the thread. It is also used to save/restore this information in push_open_tables_state()/pop_open_tables_state(). @@ -762,14 +776,17 @@ public: } }; - -/* class to save context when executing a function or trigger */ +/** + @class Sub_statement_state + @brief Used to save context when executing a function or trigger +*/ /* Defines used for Sub_statement_state::in_sub_stmt */ #define SUB_STMT_TRIGGER 1 #define SUB_STMT_FUNCTION 2 + class Sub_statement_state { public: @@ -801,7 +818,51 @@ enum enum_thread_type }; -/* +/** + This class represents the interface for internal error handlers. + Internal error handlers are exception handlers used by the server + implementation. +*/ +class Internal_error_handler +{ +protected: + Internal_error_handler() {} + virtual ~Internal_error_handler() {} + +public: + /** + Handle an error condition. + This method can be implemented by a subclass to achieve any of the + following: + - mask an error internally, prevent exposing it to the user, + - mask an error and throw another one instead. + When this method returns true, the error condition is considered + 'handled', and will not be propagated to upper layers. + It is the responsability of the code installing an internal handler + to then check for trapped conditions, and implement logic to recover + from the anticipated conditions trapped during runtime. + + This mechanism is similar to C++ try/throw/catch: + - 'try' correspond to <code>THD::push_internal_handler()</code>, + - 'throw' correspond to <code>my_error()</code>, + which invokes <code>my_message_sql()</code>, + - 'catch' correspond to checking how/if an internal handler was invoked, + before removing it from the exception stack with + <code>THD::pop_internal_handler()</code>. + + @param sql_errno the error number + @param level the error level + @param thd the calling thread + @return true if the error is handled + */ + virtual bool handle_error(uint sql_errno, + MYSQL_ERROR::enum_warning_level level, + THD *thd) = 0; +}; + + +/** + @class THD For each client connection we create a separate thread with THD serving as a thread/connection descriptor */ @@ -833,6 +894,12 @@ public: struct st_mysql_data **data_tail; void clear_data_list(); struct st_mysql_data *alloc_new_dataset(); + /* + In embedded server it points to the statement that is processed + in the current query. We store some results directly in statement + fields then. + */ + struct st_mysql_stmt *current_stmt; #endif NET net; // client connection descriptor MEM_ROOT warn_root; // For warnings and errors @@ -925,12 +992,12 @@ public: #ifndef MYSQL_CLIENT int binlog_setup_trx_data(); -#ifdef HAVE_ROW_BASED_REPLICATION - /* Public interface to write RBR events to the binlog */ void binlog_start_trans_and_stmt(); + int binlog_flush_transaction_cache(); + void binlog_set_stmt_begin(); int binlog_write_table_map(TABLE *table, bool is_transactional); int binlog_write_row(TABLE* table, bool is_transactional, MY_BITMAP const* cols, my_size_t colcnt, @@ -980,7 +1047,6 @@ public: uint get_binlog_table_maps() const { return binlog_table_maps; } -#endif /* HAVE_ROW_BASED_REPLICATION */ #endif /* MYSQL_CLIENT */ #ifndef MYSQL_CLIENT @@ -1019,9 +1085,7 @@ public: XID xid; // transaction identifier enum xa_states xa_state; // used by external XA only XID_STATE xid_state; -#ifdef HAVE_ROW_BASED_REPLICATION Rows_log_event *m_pending_rows_event; -#endif /* Tables changed in transaction (that must be invalidated in query cache). @@ -1051,7 +1115,7 @@ public: } transaction; Field *dup_field; #ifndef __WIN__ - sigset_t signals,block_signals; + sigset_t signals; #endif #ifdef SIGNAL_WITH_VIO_CLOSE Vio* active_vio; @@ -1242,7 +1306,7 @@ public: update auto-updatable fields (like auto_increment and timestamp). */ query_id_t query_id, warn_id; - ulong thread_id, col_access; + ulong col_access; #ifdef ERROR_INJECT_SUPPORT ulong error_inject_value; @@ -1251,8 +1315,8 @@ public: ulong statement_id_counter; ulong rand_saved_seed1, rand_saved_seed2; ulong row_count; // Row counter, mainly for errors and warnings - long dbug_thread_id; - pthread_t real_id; + pthread_t real_id; /* For debugging */ + my_thread_id thread_id; uint tmp_table, global_read_lock; uint server_status,open_options; enum enum_thread_type system_thread; @@ -1449,10 +1513,12 @@ public: #ifndef EMBEDDED_LIBRARY inline void clear_error() { + DBUG_ENTER("clear_error"); net.last_error[0]= 0; net.last_errno= 0; net.report_error= 0; query_error= 0; + DBUG_VOID_RETURN; } inline bool vio_ok() const { return net.vio != 0; } #else @@ -1526,7 +1592,6 @@ public: void restore_active_arena(Query_arena *set, Query_arena *backup); inline void set_current_stmt_binlog_row_based_if_mixed() { -#ifdef HAVE_ROW_BASED_REPLICATION /* If in a stored/function trigger, the caller should already have done the change. We test in_sub_stmt to prevent introducing bugs where people @@ -1539,23 +1604,17 @@ public: if ((variables.binlog_format == BINLOG_FORMAT_MIXED) && (in_sub_stmt == 0)) current_stmt_binlog_row_based= TRUE; -#endif } inline void set_current_stmt_binlog_row_based() { -#ifdef HAVE_ROW_BASED_REPLICATION current_stmt_binlog_row_based= TRUE; -#endif } inline void clear_current_stmt_binlog_row_based() { -#ifdef HAVE_ROW_BASED_REPLICATION current_stmt_binlog_row_based= FALSE; -#endif } inline void reset_current_stmt_binlog_row_based() { -#ifdef HAVE_ROW_BASED_REPLICATION /* If there are temporary tables, don't reset back to statement-based. Indeed it could be that: @@ -1570,15 +1629,15 @@ public: or trigger is decided when it starts executing, depending for example on the caller (for a stored function: if caller is SELECT or INSERT/UPDATE/DELETE...). + + Don't reset binlog format for NDB binlog injector thread. */ - if ((temporary_tables == NULL) && (in_sub_stmt == 0)) + if ((temporary_tables == NULL) && (in_sub_stmt == 0) && + (system_thread != SYSTEM_THREAD_NDBCLUSTER_BINLOG)) { current_stmt_binlog_row_based= test(variables.binlog_format == BINLOG_FORMAT_ROW); } -#else - current_stmt_binlog_row_based= FALSE; -#endif } /* @@ -1618,10 +1677,51 @@ public: return TRUE; } *p_db= strmake(db, db_length); - if (p_db_length) - *p_db_length= db_length; + *p_db_length= db_length; return FALSE; } + thd_scheduler scheduler; + +public: + /** + Add an internal error handler to the thread execution context. + @param handler the exception handler to add + */ + void push_internal_handler(Internal_error_handler *handler); + + /** + Handle an error condition. + @param sql_errno the error number + @param level the error level + @return true if the error is handled + */ + virtual bool handle_error(uint sql_errno, + MYSQL_ERROR::enum_warning_level level); + + /** + Remove the error handler last pushed. + */ + void pop_internal_handler(); + +private: + /** The current internal error handler for this thread, or NULL. */ + Internal_error_handler *m_internal_handler; + /** + The lex to hold the parsed tree of conventional (non-prepared) queries. + Whereas for prepared and stored procedure statements we use an own lex + instance for each new query, for conventional statements we reuse + the same lex. (@see mysql_parse for details). + */ + LEX main_lex; + /** + This memory root is used for two purposes: + - for conventional queries, to allocate structures stored in main_lex + during parsing, and allocate runtime data (execution plan, etc.) + during execution. + - for prepared queries, only to allocate runtime data. The parsed + tree itself is reused between executions and thus is stored elsewhere. + */ + MEM_ROOT main_mem_root; }; @@ -1633,7 +1733,7 @@ public: /* - Used to hold information about file and file structure in exchainge + Used to hold information about file and file structure in exchange via non-DB file (...INTO OUTFILE..., ...LOAD DATA...) XXX: We never call destructor for objects of this class. */ @@ -1646,6 +1746,7 @@ public: bool opt_enclosed; bool dumpfile; ulong skip_lines; + CHARSET_INFO *cs; sql_exchange(char *name,bool dumpfile_flag); }; @@ -1682,7 +1783,14 @@ public: virtual bool initialize_tables (JOIN *join=0) { return 0; } virtual void send_error(uint errcode,const char *err); virtual bool send_eof()=0; - virtual bool simple_select() { return 0; } + /** + Check if this query returns a result set and therefore is allowed in + cursors and set an error message if it is not the case. + + @retval FALSE success + @retval TRUE error, an error message is set + */ + virtual bool check_simple_select() const; virtual void abort() {} /* Cleanup instance of this class for next execution of a prepared @@ -1720,7 +1828,7 @@ public: bool send_fields(List<Item> &list, uint flags); bool send_data(List<Item> &items); bool send_eof(); - bool simple_select() { return 1; } + virtual bool check_simple_select() const { return FALSE; } void abort(); }; @@ -1797,13 +1905,13 @@ class select_create: public select_insert { HA_CREATE_INFO *create_info; Field **field; public: - select_create (TABLE_LIST *table, + select_create (TABLE_LIST *table_arg, HA_CREATE_INFO *create_info_par, List<create_field> &fields_par, List<Key> &keys_par, List<Item> &select_fields,enum_duplicates duplic, bool ignore) :select_insert (NULL, NULL, &select_fields, 0, 0, duplic, ignore), - create_table(table), extra_fields(&fields_par),keys(&keys_par), + create_table(table_arg), extra_fields(&fields_par),keys(&keys_par), create_info(create_info_par) {} int prepare(List<Item> &list, SELECT_LEX_UNIT *u); @@ -1915,7 +2023,9 @@ public: class select_singlerow_subselect :public select_subselect { public: - select_singlerow_subselect(Item_subselect *item):select_subselect(item){} + select_singlerow_subselect(Item_subselect *item_arg) + :select_subselect(item_arg) + {} bool send_data(List<Item> &items); }; @@ -1926,8 +2036,8 @@ class select_max_min_finder_subselect :public select_subselect bool (select_max_min_finder_subselect::*op)(); bool fmax; public: - select_max_min_finder_subselect(Item_subselect *item, bool mx) - :select_subselect(item), cache(0), fmax(mx) + select_max_min_finder_subselect(Item_subselect *item_arg, bool mx) + :select_subselect(item_arg), cache(0), fmax(mx) {} void cleanup(); bool send_data(List<Item> &items); @@ -1941,7 +2051,8 @@ public: class select_exists_subselect :public select_subselect { public: - select_exists_subselect(Item_subselect *item):select_subselect(item){} + select_exists_subselect(Item_subselect *item_arg) + :select_subselect(item_arg){} bool send_data(List<Item> &items); }; @@ -2039,7 +2150,8 @@ class user_var_entry class Unique :public Sql_alloc { DYNAMIC_ARRAY file_ptrs; - ulong max_elements, max_in_memory_size; + ulong max_elements; + ulonglong max_in_memory_size; IO_CACHE file; TREE tree; byte *record_pointers; @@ -2049,13 +2161,13 @@ class Unique :public Sql_alloc public: ulong elements; Unique(qsort_cmp2 comp_func, void *comp_func_fixed_arg, - uint size_arg, ulong max_in_memory_size_arg); + uint size_arg, ulonglong max_in_memory_size_arg); ~Unique(); ulong elements_in_tree() { return tree.elements_in_tree; } inline bool unique_add(void *ptr) { DBUG_ENTER("unique_add"); - DBUG_PRINT("info", ("tree %u - %u", tree.elements_in_tree, max_elements)); + DBUG_PRINT("info", ("tree %u - %lu", tree.elements_in_tree, max_elements)); if (tree.elements_in_tree > max_elements && flush()) DBUG_RETURN(1); DBUG_RETURN(!tree_insert(&tree, ptr, 0, tree.custom_arg)); @@ -2063,13 +2175,13 @@ public: bool get(TABLE *table); static double get_use_cost(uint *buffer, uint nkeys, uint key_size, - ulong max_in_memory_size); + ulonglong max_in_memory_size); inline static int get_cost_calc_buff_size(ulong nkeys, uint key_size, - ulong max_in_memory_size) + ulonglong max_in_memory_size) { - register ulong max_elems_in_tree= + register ulonglong max_elems_in_tree= (1 + max_in_memory_size / ALIGN_SIZE(sizeof(TREE_ELEMENT)+key_size)); - return sizeof(uint)*(1 + nkeys/max_elems_in_tree); + return (int) (sizeof(uint)*(1 + nkeys/max_elems_in_tree)); } void reset(); @@ -2160,13 +2272,12 @@ class select_dumpvar :public select_result_interceptor { ha_rows row_count; public: List<my_var> var_list; - List<Item_func_set_user_var> vars; - List<Item_splocal> local_vars; - select_dumpvar(void) { var_list.empty(); local_vars.empty(); vars.empty(); row_count=0;} + select_dumpvar() { var_list.empty(); row_count= 0;} ~select_dumpvar() {} int prepare(List<Item> &list, SELECT_LEX_UNIT *u); bool send_data(List<Item> &items); bool send_eof(); + virtual bool check_simple_select() const; void cleanup(); }; diff --git a/sql/sql_client.cc b/sql/sql_client.cc index 49d0d3087ad..d6f1183806e 100644 --- a/sql/sql_client.cc +++ b/sql/sql_client.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc new file mode 100644 index 00000000000..09ee4962235 --- /dev/null +++ b/sql/sql_connect.cc @@ -0,0 +1,1108 @@ +/* Copyright (C) 2007 MySQL 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 + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +/* + Functions to autenticate and handle reqests for a connection +*/ + +#include "mysql_priv.h" + +#ifdef HAVE_OPENSSL +/* + Without SSL the handshake consists of one packet. This packet + has both client capabilites and scrambled password. + With SSL the handshake might consist of two packets. If the first + packet (client capabilities) has CLIENT_SSL flag set, we have to + switch to SSL and read the second packet. The scrambled password + is in the second packet and client_capabilites field will be ignored. + Maybe it is better to accept flags other than CLIENT_SSL from the + second packet? +*/ +#define SSL_HANDSHAKE_SIZE 2 +#define NORMAL_HANDSHAKE_SIZE 6 +#define MIN_HANDSHAKE_SIZE 2 +#else +#define MIN_HANDSHAKE_SIZE 6 +#endif /* HAVE_OPENSSL */ + +#ifdef __WIN__ +static void test_signal(int sig_ptr) +{ +#if !defined( DBUG_OFF) + MessageBox(NULL,"Test signal","DBUG",MB_OK); +#endif +#if defined(OS2) + fprintf(stderr, "Test signal %d\n", sig_ptr); + fflush(stderr); +#endif +} +static void init_signals(void) +{ + int signals[7] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGBREAK,SIGABRT } ; + for (int i=0 ; i < 7 ; i++) + signal( signals[i], test_signal) ; +} +#endif + +/* + Get structure for logging connection data for the current user +*/ + +#ifndef NO_EMBEDDED_ACCESS_CHECKS +static HASH hash_user_connections; + +static int get_or_create_user_conn(THD *thd, const char *user, + const char *host, + USER_RESOURCES *mqh) +{ + int return_val= 0; + uint temp_len, user_len; + char temp_user[USER_HOST_BUFF_SIZE]; + struct user_conn *uc; + + DBUG_ASSERT(user != 0); + DBUG_ASSERT(host != 0); + + user_len= strlen(user); + temp_len= (strmov(strmov(temp_user, user)+1, host) - temp_user)+1; + (void) pthread_mutex_lock(&LOCK_user_conn); + if (!(uc = (struct user_conn *) hash_search(&hash_user_connections, + (byte*) temp_user, temp_len))) + { + /* First connection for user; Create a user connection object */ + if (!(uc= ((struct user_conn*) + my_malloc(sizeof(struct user_conn) + temp_len+1, + MYF(MY_WME))))) + { + net_send_error(thd, 0, NullS); // Out of memory + return_val= 1; + goto end; + } + uc->user=(char*) (uc+1); + memcpy(uc->user,temp_user,temp_len+1); + uc->host= uc->user + user_len + 1; + uc->len= temp_len; + uc->connections= uc->questions= uc->updates= uc->conn_per_hour= 0; + uc->user_resources= *mqh; + uc->intime= thd->thr_create_time; + if (my_hash_insert(&hash_user_connections, (byte*) uc)) + { + my_free((char*) uc,0); + net_send_error(thd, 0, NullS); // Out of memory + return_val= 1; + goto end; + } + } + thd->user_connect=uc; + uc->connections++; +end: + (void) pthread_mutex_unlock(&LOCK_user_conn); + return return_val; + +} + + +/* + check if user has already too many connections + + SYNOPSIS + check_for_max_user_connections() + thd Thread handle + uc User connect object + + NOTES + If check fails, we decrease user connection count, which means one + shouldn't call decrease_user_connections() after this function. + + RETURN + 0 ok + 1 error +*/ + +int check_for_max_user_connections(THD *thd, USER_CONN *uc) +{ + int error=0; + DBUG_ENTER("check_for_max_user_connections"); + + (void) pthread_mutex_lock(&LOCK_user_conn); + if (max_user_connections && !uc->user_resources.user_conn && + max_user_connections < (uint) uc->connections) + { + net_printf_error(thd, ER_TOO_MANY_USER_CONNECTIONS, uc->user); + error=1; + goto end; + } + time_out_user_resource_limits(thd, uc); + if (uc->user_resources.user_conn && + uc->user_resources.user_conn < uc->connections) + { + net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, + "max_user_connections", + (long) uc->user_resources.user_conn); + error= 1; + goto end; + } + if (uc->user_resources.conn_per_hour && + uc->user_resources.conn_per_hour <= uc->conn_per_hour) + { + net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, + "max_connections_per_hour", + (long) uc->user_resources.conn_per_hour); + error=1; + goto end; + } + uc->conn_per_hour++; + + end: + if (error) + uc->connections--; // no need for decrease_user_connections() here + (void) pthread_mutex_unlock(&LOCK_user_conn); + DBUG_RETURN(error); +} + + +/* + Decrease user connection count + + SYNOPSIS + decrease_user_connections() + uc User connection object + + NOTES + If there is a n user connection object for a connection + (which only happens if 'max_user_connections' is defined or + if someone has created a resource grant for a user), then + the connection count is always incremented on connect. + + The user connect object is not freed if some users has + 'max connections per hour' defined as we need to be able to hold + count over the lifetime of the connection. +*/ + +void decrease_user_connections(USER_CONN *uc) +{ + DBUG_ENTER("decrease_user_connections"); + (void) pthread_mutex_lock(&LOCK_user_conn); + DBUG_ASSERT(uc->connections); + if (!--uc->connections && !mqh_used) + { + /* Last connection for user; Delete it */ + (void) hash_delete(&hash_user_connections,(byte*) uc); + } + (void) pthread_mutex_unlock(&LOCK_user_conn); + DBUG_VOID_RETURN; +} + + +/* + Reset per-hour user resource limits when it has been more than + an hour since they were last checked + + SYNOPSIS: + time_out_user_resource_limits() + thd Thread handler + uc User connection details + + NOTE: + This assumes that the LOCK_user_conn mutex has been acquired, so it is + safe to test and modify members of the USER_CONN structure. +*/ + +void time_out_user_resource_limits(THD *thd, USER_CONN *uc) +{ + time_t check_time = thd->start_time ? thd->start_time : time(NULL); + DBUG_ENTER("time_out_user_resource_limits"); + + /* If more than a hour since last check, reset resource checking */ + if (check_time - uc->intime >= 3600) + { + uc->questions=1; + uc->updates=0; + uc->conn_per_hour=0; + uc->intime=check_time; + } + + DBUG_VOID_RETURN; +} + +/* + Check if maximum queries per hour limit has been reached + returns 0 if OK. +*/ + +bool check_mqh(THD *thd, uint check_command) +{ + bool error= 0; + USER_CONN *uc=thd->user_connect; + DBUG_ENTER("check_mqh"); + DBUG_ASSERT(uc != 0); + + (void) pthread_mutex_lock(&LOCK_user_conn); + + time_out_user_resource_limits(thd, uc); + + /* Check that we have not done too many questions / hour */ + if (uc->user_resources.questions && + uc->questions++ >= uc->user_resources.questions) + { + net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_questions", + (long) uc->user_resources.questions); + error=1; + goto end; + } + if (check_command < (uint) SQLCOM_END) + { + /* Check that we have not done too many updates / hour */ + if (uc->user_resources.updates && + (sql_command_flags[check_command] & CF_CHANGES_DATA) && + uc->updates++ >= uc->user_resources.updates) + { + net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_updates", + (long) uc->user_resources.updates); + error=1; + goto end; + } + } +end: + (void) pthread_mutex_unlock(&LOCK_user_conn); + DBUG_RETURN(error); +} + +#endif /* NO_EMBEDDED_ACCESS_CHECKS */ + + +/* + Check if user exist and password supplied is correct. + + SYNOPSIS + check_user() + thd thread handle, thd->security_ctx->{host,user,ip} are used + command originator of the check: now check_user is called + during connect and change user procedures; used for + logging. + passwd scrambled password received from client + passwd_len length of scrambled password + db database name to connect to, may be NULL + check_count dont know exactly + + Note, that host, user and passwd may point to communication buffer. + Current implementation does not depend on that, but future changes + should be done with this in mind; 'thd' is INOUT, all other params + are 'IN'. + + RETURN VALUE + 0 OK; thd->security_ctx->user/master_access/priv_user/db_access and + thd->db are updated; OK is sent to client; + -1 access denied or handshake error; error is sent to client; + >0 error, not sent to client +*/ + +int check_user(THD *thd, enum enum_server_command command, + const char *passwd, uint passwd_len, const char *db, + bool check_count) +{ + DBUG_ENTER("check_user"); + +#ifdef NO_EMBEDDED_ACCESS_CHECKS + thd->main_security_ctx.master_access= GLOBAL_ACLS; // Full rights + /* Change database if necessary */ + if (db && db[0]) + { + /* + thd->db is saved in caller and needs to be freed by caller if this + function returns 0 + */ + thd->reset_db(NULL, 0); + if (mysql_change_db(thd, db, FALSE)) + { + /* Send the error to the client */ + net_send_error(thd); + DBUG_RETURN(-1); + } + } + send_ok(thd); + DBUG_RETURN(0); +#else + + my_bool opt_secure_auth_local; + pthread_mutex_lock(&LOCK_global_system_variables); + opt_secure_auth_local= opt_secure_auth; + pthread_mutex_unlock(&LOCK_global_system_variables); + + /* + If the server is running in secure auth mode, short scrambles are + forbidden. + */ + if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323) + { + net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE); + general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE)); + DBUG_RETURN(-1); + } + if (passwd_len != 0 && + passwd_len != SCRAMBLE_LENGTH && + passwd_len != SCRAMBLE_LENGTH_323) + DBUG_RETURN(ER_HANDSHAKE_ERROR); + + /* + Clear thd->db as it points to something, that will be freed when + connection is closed. We don't want to accidentally free a wrong pointer + if connect failed. Also in case of 'CHANGE USER' failure, current + database will be switched to 'no database selected'. + */ + thd->reset_db(NULL, 0); + + USER_RESOURCES ur; + int res= acl_getroot(thd, &ur, passwd, passwd_len); +#ifndef EMBEDDED_LIBRARY + if (res == -1) + { + /* + This happens when client (new) sends password scrambled with + scramble(), but database holds old value (scrambled with + scramble_323()). Here we please client to send scrambled_password + in old format. + */ + NET *net= &thd->net; + if (opt_secure_auth_local) + { + net_printf_error(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE, + thd->main_security_ctx.user, + thd->main_security_ctx.host_or_ip); + general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE), + thd->main_security_ctx.user, + thd->main_security_ctx.host_or_ip); + DBUG_RETURN(-1); + } + /* We have to read very specific packet size */ + if (send_old_password_request(thd) || + my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) + { + inc_host_errors(&thd->remote.sin_addr); + DBUG_RETURN(ER_HANDSHAKE_ERROR); + } + /* Final attempt to check the user based on reply */ + /* So as passwd is short, errcode is always >= 0 */ + res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323); + } +#endif /*EMBEDDED_LIBRARY*/ + /* here res is always >= 0 */ + if (res == 0) + { + if (!(thd->main_security_ctx.master_access & + NO_ACCESS)) // authentication is OK + { + DBUG_PRINT("info", + ("Capabilities: %lu packet_length: %ld Host: '%s' " + "Login user: '%s' Priv_user: '%s' Using password: %s " + "Access: %lu db: '%s'", + thd->client_capabilities, + thd->max_client_packet_length, + thd->main_security_ctx.host_or_ip, + thd->main_security_ctx.user, + thd->main_security_ctx.priv_user, + passwd_len ? "yes": "no", + thd->main_security_ctx.master_access, + (thd->db ? thd->db : "*none*"))); + + if (check_count) + { + VOID(pthread_mutex_lock(&LOCK_thread_count)); + bool count_ok= thread_count <= max_connections + delayed_insert_threads + || (thd->main_security_ctx.master_access & SUPER_ACL); + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + if (!count_ok) + { // too many connections + net_send_error(thd, ER_CON_COUNT_ERROR); + DBUG_RETURN(-1); + } + } + + /* + Log the command before authentication checks, so that the user can + check the log for the tried login tried and also to detect + break-in attempts. + */ + general_log_print(thd, command, + (thd->main_security_ctx.priv_user == + thd->main_security_ctx.user ? + (char*) "%s@%s on %s" : + (char*) "%s@%s as anonymous on %s"), + thd->main_security_ctx.user, + thd->main_security_ctx.host_or_ip, + db ? db : (char*) ""); + + /* + This is the default access rights for the current database. It's + set to 0 here because we don't have an active database yet (and we + may not have an active database to set. + */ + thd->main_security_ctx.db_access=0; + + /* Don't allow user to connect if he has done too many queries */ + if ((ur.questions || ur.updates || ur.conn_per_hour || ur.user_conn || + max_user_connections) && + get_or_create_user_conn(thd, + (opt_old_style_user_limits ? thd->main_security_ctx.user : + thd->main_security_ctx.priv_user), + (opt_old_style_user_limits ? thd->main_security_ctx.host_or_ip : + thd->main_security_ctx.priv_host), + &ur)) + DBUG_RETURN(-1); + if (thd->user_connect && + (thd->user_connect->user_resources.conn_per_hour || + thd->user_connect->user_resources.user_conn || + max_user_connections) && + check_for_max_user_connections(thd, thd->user_connect)) + DBUG_RETURN(-1); + + /* Change database if necessary */ + if (db && db[0]) + { + if (mysql_change_db(thd, db, FALSE)) + { + /* Send error to the client */ + net_send_error(thd); + if (thd->user_connect) + decrease_user_connections(thd->user_connect); + DBUG_RETURN(-1); + } + } + send_ok(thd); + thd->password= test(passwd_len); // remember for error messages + /* Ready to handle queries */ + DBUG_RETURN(0); + } + } + else if (res == 2) // client gave short hash, server has long hash + { + net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE); + general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE)); + DBUG_RETURN(-1); + } + net_printf_error(thd, ER_ACCESS_DENIED_ERROR, + thd->main_security_ctx.user, + thd->main_security_ctx.host_or_ip, + passwd_len ? ER(ER_YES) : ER(ER_NO)); + general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR), + thd->main_security_ctx.user, + thd->main_security_ctx.host_or_ip, + passwd_len ? ER(ER_YES) : ER(ER_NO)); + DBUG_RETURN(-1); +#endif /* NO_EMBEDDED_ACCESS_CHECKS */ +} + + +/* + Check for maximum allowable user connections, if the mysqld server is + started with corresponding variable that is greater then 0. +*/ + +extern "C" byte *get_key_conn(user_conn *buff, uint *length, + my_bool not_used __attribute__((unused))) +{ + *length=buff->len; + return (byte*) buff->user; +} + + +extern "C" void free_user(struct user_conn *uc) +{ + my_free((char*) uc,MYF(0)); +} + + +void init_max_user_conn(void) +{ +#ifndef NO_EMBEDDED_ACCESS_CHECKS + (void) hash_init(&hash_user_connections,system_charset_info,max_connections, + 0,0, + (hash_get_key) get_key_conn, (hash_free_key) free_user, + 0); +#endif +} + + +void free_max_user_conn(void) +{ +#ifndef NO_EMBEDDED_ACCESS_CHECKS + hash_free(&hash_user_connections); +#endif /* NO_EMBEDDED_ACCESS_CHECKS */ +} + + +void reset_mqh(LEX_USER *lu, bool get_them= 0) +{ +#ifndef NO_EMBEDDED_ACCESS_CHECKS + (void) pthread_mutex_lock(&LOCK_user_conn); + if (lu) // for GRANT + { + USER_CONN *uc; + uint temp_len=lu->user.length+lu->host.length+2; + char temp_user[USER_HOST_BUFF_SIZE]; + + memcpy(temp_user,lu->user.str,lu->user.length); + memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length); + temp_user[lu->user.length]='\0'; temp_user[temp_len-1]=0; + if ((uc = (struct user_conn *) hash_search(&hash_user_connections, + (byte*) temp_user, temp_len))) + { + uc->questions=0; + get_mqh(temp_user,&temp_user[lu->user.length+1],uc); + uc->updates=0; + uc->conn_per_hour=0; + } + } + else + { + /* for FLUSH PRIVILEGES and FLUSH USER_RESOURCES */ + for (uint idx=0;idx < hash_user_connections.records; idx++) + { + USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections, + idx); + if (get_them) + get_mqh(uc->user,uc->host,uc); + uc->questions=0; + uc->updates=0; + uc->conn_per_hour=0; + } + } + (void) pthread_mutex_unlock(&LOCK_user_conn); +#endif /* NO_EMBEDDED_ACCESS_CHECKS */ +} + + +void thd_init_client_charset(THD *thd, uint cs_number) +{ + /* + Use server character set and collation if + - opt_character_set_client_handshake is not set + - client has not specified a character set + - client character set is the same as the servers + - client character set doesn't exists in server + */ + if (!opt_character_set_client_handshake || + !(thd->variables.character_set_client= get_charset(cs_number, MYF(0))) || + !my_strcasecmp(&my_charset_latin1, + global_system_variables.character_set_client->name, + thd->variables.character_set_client->name)) + { + thd->variables.character_set_client= + global_system_variables.character_set_client; + thd->variables.collation_connection= + global_system_variables.collation_connection; + thd->variables.character_set_results= + global_system_variables.character_set_results; + } + else + { + thd->variables.character_set_results= + thd->variables.collation_connection= + thd->variables.character_set_client; + } +} + + +/* + Initialize connection threads +*/ + +bool init_new_connection_handler_thread() +{ + pthread_detach_this_thread(); +#if defined(__WIN__) + init_signals(); +#else + /* Win32 calls this in pthread_create */ + if (my_thread_init()) + return 1; +#endif /* __WIN__ */ + return 0; +} + +/* + Perform handshake, authorize client and update thd ACL variables. + + SYNOPSIS + check_connection() + thd thread handle + + RETURN + 0 success, OK is sent to user, thd is updated. + -1 error, which is sent to user + > 0 error code (not sent to user) +*/ + +#ifndef EMBEDDED_LIBRARY +static int check_connection(THD *thd) +{ + uint connect_errors= 0; + NET *net= &thd->net; + ulong pkt_len= 0; + char *end; + + DBUG_PRINT("info", + ("New connection received on %s", vio_description(net->vio))); +#ifdef SIGNAL_WITH_VIO_CLOSE + thd->set_active_vio(net->vio); +#endif + + if (!thd->main_security_ctx.host) // If TCP/IP connection + { + char ip[30]; + + if (vio_peer_addr(net->vio, ip, &thd->peer_port)) + return (ER_BAD_HOST_ERROR); + if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(0)))) + return (ER_OUT_OF_RESOURCES); + thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip; + vio_in_addr(net->vio,&thd->remote.sin_addr); + if (!(specialflag & SPECIAL_NO_RESOLVE)) + { + vio_in_addr(net->vio,&thd->remote.sin_addr); + thd->main_security_ctx.host= + ip_to_hostname(&thd->remote.sin_addr, &connect_errors); + /* Cut very long hostnames to avoid possible overflows */ + if (thd->main_security_ctx.host) + { + if (thd->main_security_ctx.host != my_localhost) + thd->main_security_ctx.host[min(strlen(thd->main_security_ctx.host), + HOSTNAME_LENGTH)]= 0; + thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host; + } + if (connect_errors > max_connect_errors) + return(ER_HOST_IS_BLOCKED); + } + DBUG_PRINT("info",("Host: %s ip: %s", + (thd->main_security_ctx.host ? + thd->main_security_ctx.host : "unknown host"), + (thd->main_security_ctx.ip ? + thd->main_security_ctx.ip : "unknown ip"))); + if (acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip)) + return(ER_HOST_NOT_PRIVILEGED); + } + else /* Hostname given means that the connection was on a socket */ + { + DBUG_PRINT("info",("Host: %s", thd->main_security_ctx.host)); + thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host; + thd->main_security_ctx.ip= 0; + /* Reset sin_addr */ + bzero((char*) &thd->remote, sizeof(thd->remote)); + } + vio_keepalive(net->vio, TRUE); + { + /* buff[] needs to big enough to hold the server_version variable */ + char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64]; + ulong client_flags = (CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB | + CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION); + + if (opt_using_transactions) + client_flags|=CLIENT_TRANSACTIONS; +#ifdef HAVE_COMPRESS + client_flags |= CLIENT_COMPRESS; +#endif /* HAVE_COMPRESS */ +#ifdef HAVE_OPENSSL + if (ssl_acceptor_fd) + client_flags |= CLIENT_SSL; /* Wow, SSL is available! */ +#endif /* HAVE_OPENSSL */ + + end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1; + int4store((uchar*) end, thd->thread_id); + end+= 4; + /* + So as check_connection is the only entry point to authorization + procedure, scramble is set here. This gives us new scramble for + each handshake. + */ + create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand); + /* + Old clients does not understand long scrambles, but can ignore packet + tail: that's why first part of the scramble is placed here, and second + part at the end of packet. + */ + end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1; + + int2store(end, client_flags); + /* write server characteristics: up to 16 bytes allowed */ + end[2]=(char) default_charset_info->number; + int2store(end+3, thd->server_status); + bzero(end+5, 13); + end+= 18; + /* write scramble tail */ + end= strmake(end, thd->scramble + SCRAMBLE_LENGTH_323, + SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323) + 1; + + /* At this point we write connection message and read reply */ + if (net_write_command(net, (uchar) protocol_version, "", 0, buff, + (uint) (end-buff)) || + (pkt_len= my_net_read(net)) == packet_error || + pkt_len < MIN_HANDSHAKE_SIZE) + { + inc_host_errors(&thd->remote.sin_addr); + return(ER_HANDSHAKE_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(ER_OUT_OF_RESOURCES); + + thd->client_capabilities=uint2korr(net->read_pos); + if (thd->client_capabilities & CLIENT_PROTOCOL_41) + { + thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16; + thd->max_client_packet_length= uint4korr(net->read_pos+4); + DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8])); + thd_init_client_charset(thd, (uint) net->read_pos[8]); + thd->update_charset(); + end= (char*) net->read_pos+32; + } + else + { + thd->max_client_packet_length= uint3korr(net->read_pos+2); + end= (char*) net->read_pos+5; + } + + if (thd->client_capabilities & CLIENT_IGNORE_SPACE) + thd->variables.sql_mode|= MODE_IGNORE_SPACE; +#ifdef HAVE_OPENSSL + DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities)); + if (thd->client_capabilities & CLIENT_SSL) + { + /* Do the SSL layering. */ + if (!ssl_acceptor_fd) + { + inc_host_errors(&thd->remote.sin_addr); + return(ER_HANDSHAKE_ERROR); + } + DBUG_PRINT("info", ("IO layer change in progress...")); + if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout)) + { + DBUG_PRINT("error", ("Failed to accept new SSL connection")); + inc_host_errors(&thd->remote.sin_addr); + return(ER_HANDSHAKE_ERROR); + } + DBUG_PRINT("info", ("Reading user information over SSL layer")); + if ((pkt_len= my_net_read(net)) == packet_error || + pkt_len < NORMAL_HANDSHAKE_SIZE) + { + DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)", + pkt_len)); + inc_host_errors(&thd->remote.sin_addr); + return(ER_HANDSHAKE_ERROR); + } + } +#endif /* HAVE_OPENSSL */ + + if (end >= (char*) net->read_pos+ pkt_len +2) + { + inc_host_errors(&thd->remote.sin_addr); + return(ER_HANDSHAKE_ERROR); + } + + if (thd->client_capabilities & CLIENT_INTERACTIVE) + thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout; + if ((thd->client_capabilities & CLIENT_TRANSACTIONS) && + opt_using_transactions) + net->return_status= &thd->server_status; + + char *user= end; + char *passwd= strend(user)+1; + uint user_len= passwd - user - 1; + char *db= passwd; + char db_buff[NAME_LEN + 1]; // buffer to store db in utf8 + char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8 + uint dummy_errors; + + /* + Old clients send null-terminated string as password; new clients send + the size (1 byte) + string (not null-terminated). Hence in case of empty + password both send '\0'. + + This strlen() can't be easily deleted without changing protocol. + */ + uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? + *passwd++ : strlen(passwd); + db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ? + db + passwd_len + 1 : 0; + /* strlen() can't be easily deleted without changing protocol */ + uint db_len= db ? strlen(db) : 0; + + if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len) + { + inc_host_errors(&thd->remote.sin_addr); + return ER_HANDSHAKE_ERROR; + } + + /* Since 4.1 all database names are stored in utf8 */ + if (db) + { + db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1, + system_charset_info, + db, db_len, + thd->charset(), &dummy_errors)]= 0; + db= db_buff; + } + + user_buff[user_len= copy_and_convert(user_buff, sizeof(user_buff)-1, + system_charset_info, user, user_len, + thd->charset(), &dummy_errors)]= '\0'; + user= user_buff; + + /* If username starts and ends in "'", chop them off */ + if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'') + { + user[user_len-1]= 0; + user++; + user_len-= 2; + } + + if (thd->main_security_ctx.user) + x_free(thd->main_security_ctx.user); + if (!(thd->main_security_ctx.user= my_strdup(user, MYF(0)))) + return (ER_OUT_OF_RESOURCES); + return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE); +} + + +/* + Setup thread to be used with the current thread + + SYNOPSIS + bool setup_connection_thread_globals() + thd Thread/connection handler + + RETURN + 0 ok + 1 Error (out of memory) + In this case we will close the connection and increment status +*/ + +bool setup_connection_thread_globals(THD *thd) +{ + if (thd->store_globals()) + { + close_connection(thd, ER_OUT_OF_RESOURCES, 1); + statistic_increment(aborted_connects,&LOCK_status); + thread_scheduler.end_thread(thd, 0); + return 1; // Error + } + return 0; +} + + +/* + Autenticate user, with error reporting + + SYNOPSIS + login_connection() + thd Thread handler + + NOTES + Connection is not closed in case of errors + + RETURN + 0 ok + 1 error +*/ + + +bool login_connection(THD *thd) +{ + int error; + NET *net= &thd->net; + Security_context *sctx= thd->security_ctx; + DBUG_ENTER("login_connection"); + DBUG_PRINT("info", ("handle_one_connection called by thread %lu", + thd->thread_id)); + + net->no_send_error= 0; + + /* Use "connect_timeout" value during connection phase */ + net_set_read_timeout(net, connect_timeout); + net_set_write_timeout(net, connect_timeout); + + if ((error=check_connection(thd))) + { // Wrong permissions + if (error > 0) + net_printf_error(thd, error, sctx->host_or_ip); +#ifdef __NT__ + if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE) + my_sleep(1000); /* must wait after eof() */ +#endif + statistic_increment(aborted_connects,&LOCK_status); + DBUG_RETURN(1); + } + /* Connect completed, set read/write timeouts back to default */ + net_set_read_timeout(net, thd->variables.net_read_timeout); + net_set_write_timeout(net, thd->variables.net_write_timeout); + DBUG_RETURN(0); +} + + +/* + Close an established connection + + NOTES + This mainly updates status variables +*/ + +void end_connection(THD *thd) +{ + NET *net= &thd->net; + if (thd->user_connect) + decrease_user_connections(thd->user_connect); + if (net->error && net->vio != 0 && net->report_error) + { + Security_context *sctx= thd->security_ctx; + if (!thd->killed && thd->variables.log_warnings > 1) + sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), + thd->thread_id,(thd->db ? thd->db : "unconnected"), + sctx->user ? sctx->user : "unauthenticated", + sctx->host_or_ip, + (net->last_errno ? ER(net->last_errno) : + ER(ER_UNKNOWN_ERROR))); + net_send_error(thd, net->last_errno, NullS); + statistic_increment(aborted_threads,&LOCK_status); + } + else if (thd->killed) + statistic_increment(aborted_threads,&LOCK_status); +} + + +/* + Initialize THD to handle queries +*/ + +void prepare_new_connection_state(THD* thd) +{ + Security_context *sctx= thd->security_ctx; + +#ifdef __NETWARE__ + netware_reg_user(sctx->ip, sctx->user, "MySQL"); +#endif + + if (thd->variables.max_join_size == HA_POS_ERROR) + thd->options |= OPTION_BIG_SELECTS; + if (thd->client_capabilities & CLIENT_COMPRESS) + thd->net.compress=1; // Use compression + + thd->version= refresh_version; + thd->proc_info= 0; + thd->command= COM_SLEEP; + thd->set_time(); + thd->init_for_queries(); + + if (sys_init_connect.value_length && !(sctx->master_access & SUPER_ACL)) + { + execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect); + if (thd->query_error) + { + thd->killed= THD::KILL_CONNECTION; + sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), + thd->thread_id,(thd->db ? thd->db : "unconnected"), + sctx->user ? sctx->user : "unauthenticated", + sctx->host_or_ip, "init_connect command failed"); + sql_print_warning("%s", thd->net.last_error); + } + thd->proc_info=0; + thd->set_time(); + thd->init_for_queries(); + } +} + + +/* + Thread handler for a connection + + SYNOPSIS + handle_one_connection() + arg Connection object (THD) + + IMPLEMENTATION + This function (normally) does the following: + - Initialize thread + - Initialize THD to be used with this thread + - Authenticate user + - Execute all queries sent on the connection + - Take connection down + - End thread / Handle next connection using thread from thread cache +*/ + +pthread_handler_t handle_one_connection(void *arg) +{ + THD *thd= (THD*) arg; + uint launch_time = + (uint) ((thd->thr_create_time = time(NULL)) - thd->connect_time); + + if (thread_scheduler.init_new_connection_thread()) + { + close_connection(thd, ER_OUT_OF_RESOURCES, 1); + statistic_increment(aborted_connects,&LOCK_status); + thread_scheduler.end_thread(thd,0); + return 0; + } + if (launch_time >= slow_launch_time) + statistic_increment(slow_launch_threads,&LOCK_status); + + /* + handle_one_connection() is normally the only way a thread would + start and would always be on the very high end of the stack , + therefore, the thread stack always starts at the address of the + first local variable of handle_one_connection, which is thd. We + need to know the start of the stack so that we could check for + stack overruns. + */ + thd->thread_stack= (char*) &thd; + if (setup_connection_thread_globals(thd)) + return 0; + + for (;;) + { + NET *net= &thd->net; + + if (login_connection(thd)) + goto end_thread; + + prepare_new_connection_state(thd); + + while (!net->error && net->vio != 0 && + !(thd->killed == THD::KILL_CONNECTION)) + { + net->no_send_error= 0; + if (do_command(thd)) + break; + } + end_connection(thd); + +end_thread: + close_connection(thd, 0, 1); + if (thread_scheduler.end_thread(thd,1)) + return 0; // Probably no-threads + + /* + If end_thread() returns, we are either running with + thread-handler=no-threads or this thread has been schedule to + handle the next connection. + */ + thd= current_thd; + thd->thread_stack= (char*) &thd; + } +} +#endif /* EMBEDDED_LIBRARY */ diff --git a/sql/sql_crypt.cc b/sql/sql_crypt.cc index 4e73941fb49..ebd424f00f0 100644 --- a/sql/sql_crypt.cc +++ b/sql/sql_crypt.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2001, 2003, 2005 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_crypt.h b/sql/sql_crypt.h index 25bc2d29e1d..f3db9adde25 100644 --- a/sql/sql_crypt.h +++ b/sql/sql_crypt.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2001, 2005 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index 46fae3ffb99..d31c0af1163 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2005 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2005-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_cursor.h b/sql/sql_cursor.h index d1156dfba8d..6edd6b24b36 100644 --- a/sql/sql_cursor.h +++ b/sql/sql_cursor.h @@ -1,11 +1,8 @@ -#ifndef _sql_cursor_h_ -#define _sql_cursor_h_ -/* Copyright (C) 2005 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2005 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -16,6 +13,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef _sql_cursor_h_ +#define _sql_cursor_h_ + #ifdef USE_PRAGMA_INTERFACE #pragma interface /* gcc class interface */ #endif diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 3960236e828..4fd35b7e6e8 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -705,6 +704,7 @@ bool mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info, qinfo.db = db; qinfo.db_len = strlen(db); + /* These DDL methods and logging protected with LOCK_mysql_create_db */ mysql_bin_log.write(&qinfo); } send_ok(thd, result); @@ -783,6 +783,7 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info) qinfo.db_len = strlen(db); thd->clear_error(); + /* These DDL methods and logging protected with LOCK_mysql_create_db */ mysql_bin_log.write(&qinfo); } send_ok(thd, result); @@ -905,6 +906,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) qinfo.db_len = strlen(db); thd->clear_error(); + /* These DDL methods and logging protected with LOCK_mysql_create_db */ mysql_bin_log.write(&qinfo); } thd->server_status|= SERVER_STATUS_DB_DROPPED; @@ -931,6 +933,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) tbl_name_len= strlen(tbl->table_name) + 3; if (query_pos + tbl_name_len + 1 >= query_end) { + /* These DDL methods and logging protected with LOCK_mysql_create_db */ write_to_binlog(thd, query, query_pos -1 - query, db, db_len); query_pos= query_data_start; } @@ -943,6 +946,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) if (query_pos != query_data_start) { + /* These DDL methods and logging protected with LOCK_mysql_create_db */ write_to_binlog(thd, query, query_pos -1 - query, db, db_len); } } @@ -1297,8 +1301,7 @@ err: bool mysql_change_db(THD *thd, const char *name, bool no_access_check) { - int path_length, db_length; - char *db_name; + LEX_STRING db_name; bool system_db= 0; #ifndef NO_EMBEDDED_ACCESS_CHECKS ulong db_access; @@ -1318,25 +1321,26 @@ bool mysql_change_db(THD *thd, const char *name, bool no_access_check) /* Called from SP to restore the original database, which was NULL */ DBUG_ASSERT(no_access_check); system_db= 1; - db_name= NULL; - db_length= 0; + db_name.str= NULL; + db_name.length= 0; goto end; } /* Now we need to make a copy because check_db_name requires a non-constant argument. TODO: fix check_db_name. */ - if ((db_name= my_strdup(name, MYF(MY_WME))) == NULL) + if ((db_name.str= my_strdup(name, MYF(MY_WME))) == NULL) DBUG_RETURN(1); /* the error is set */ - db_length= strlen(db_name); - if (check_db_name(db_name)) + db_name.length= strlen(db_name.str); + if (check_db_name(&db_name)) { - my_error(ER_WRONG_DB_NAME, MYF(0), db_name); - my_free(db_name, MYF(0)); + my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str); + my_free(db_name.str, MYF(0)); DBUG_RETURN(1); } - DBUG_PRINT("info",("Use database: %s", db_name)); - if (!my_strcasecmp(system_charset_info, db_name, information_schema_name.str)) + DBUG_PRINT("info",("Use database: %s", db_name.str)); + if (!my_strcasecmp(system_charset_info, db_name.str, + information_schema_name.str)) { system_db= 1; #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -1351,34 +1355,35 @@ bool mysql_change_db(THD *thd, const char *name, bool no_access_check) if (test_all_bits(sctx->master_access, DB_ACLS)) db_access=DB_ACLS; else - db_access= (acl_get(sctx->host, sctx->ip, sctx->priv_user, db_name, 0) | + db_access= (acl_get(sctx->host, sctx->ip, sctx->priv_user, + db_name.str, 0) | sctx->master_access); if (!(db_access & DB_ACLS) && (!grant_option || - check_grant_db(thd,db_name))) + check_grant_db(thd, db_name.str))) { my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->priv_host, - db_name); + db_name.str); general_log_print(thd, COM_INIT_DB, ER(ER_DBACCESS_DENIED_ERROR), - sctx->priv_user, sctx->priv_host, db_name); - my_free(db_name,MYF(0)); + sctx->priv_user, sctx->priv_host, db_name.str); + my_free(db_name.str, MYF(0)); DBUG_RETURN(1); } } #endif - if (check_db_dir_existence(db_name)) + if (check_db_dir_existence(db_name.str)) { - my_error(ER_BAD_DB_ERROR, MYF(0), db_name); - my_free(db_name, MYF(0)); + my_error(ER_BAD_DB_ERROR, MYF(0), db_name.str); + my_free(db_name.str, MYF(0)); DBUG_RETURN(1); } end: x_free(thd->db); - DBUG_ASSERT(db_name == NULL || db_name[0] != '\0'); - thd->reset_db(db_name, db_length); // THD::~THD will free this + DBUG_ASSERT(db_name.str == NULL || db_name.str[0] != '\0'); + thd->reset_db(db_name.str, db_name.length); // THD::~THD will free this #ifndef NO_EMBEDDED_ACCESS_CHECKS if (!no_access_check) sctx->db_access= db_access; @@ -1392,7 +1397,7 @@ end: { HA_CREATE_INFO create; - load_db_opt_by_name(thd, db_name, &create); + load_db_opt_by_name(thd, db_name.str, &create); thd->db_charset= create.default_table_charset ? create.default_table_charset : diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index f128b5e8706..07313c954ba 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -70,13 +69,13 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, Test if the user wants to delete all rows and deletion doesn't have any side-effects (because of triggers), so we can use optimized handler::delete_all_rows() method. - - If row-based replication is used, we also delete the table row by - row. + We implement fast TRUNCATE for InnoDB even if triggers are present. + TRUNCATE ignores triggers. */ if (!using_limit && const_cond && (!conds || conds->val_int()) && !(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) && - !(table->triggers && table->triggers->has_delete_triggers()) && + (thd->lex->sql_command == SQLCOM_TRUNCATE || + !(table->triggers && table->triggers->has_delete_triggers())) && !thd->current_stmt_binlog_row_based) { /* Update the table->file->stats.records number */ @@ -154,7 +153,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (order && order->elements) { - uint length; + uint length= 0; SORT_FIELD *sortorder; TABLE_LIST tables; List<Item> fields; @@ -174,7 +173,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, DBUG_RETURN(TRUE); } - if (!select && limit != HA_POS_ERROR) + if ((!select || table->quick_keys.is_clear_all()) && limit != HA_POS_ERROR) usable_index= get_index_for_order(table, (ORDER*)(order->first), limit); if (usable_index == MAX_KEY) @@ -350,11 +349,11 @@ cleanup: mysql_unlock_tables(thd, thd->lock); thd->lock=0; } - if (error < 0) + if (error < 0 || (thd->lex->ignore && !thd->is_fatal_error)) { thd->row_count_func= deleted; send_ok(thd,deleted); - DBUG_PRINT("info",("%d records deleted",deleted)); + DBUG_PRINT("info",("%ld records deleted",(long) deleted)); } DBUG_RETURN(error >= 0 || thd->net.report_error); } @@ -378,6 +377,7 @@ bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) Item *fake_conds= 0; SELECT_LEX *select_lex= &thd->lex->select_lex; DBUG_ENTER("mysql_prepare_delete"); + List<Item> all_fields; thd->lex->allow_sum_func= 0; if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, @@ -395,12 +395,17 @@ bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) } { TABLE_LIST *duplicate; - if ((duplicate= unique_table(thd, table_list, table_list->next_global))) + if ((duplicate= unique_table(thd, table_list, table_list->next_global, 0))) { update_non_unique_table_error(table_list, "DELETE", duplicate); DBUG_RETURN(TRUE); } } + + if (select_lex->inner_refs_list.elements && + fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array)) + DBUG_RETURN(-1); + select_lex->fix_prepare_information(thd, conds, &fake_conds); DBUG_RETURN(FALSE); } @@ -487,7 +492,7 @@ bool mysql_multi_delete_prepare(THD *thd) { TABLE_LIST *duplicate; if ((duplicate= unique_table(thd, target_tbl->correspondent_table, - lex->query_tables))) + lex->query_tables, 0))) { update_non_unique_table_error(target_tbl->correspondent_table, "DELETE", duplicate); @@ -862,6 +867,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) bool error; uint closed_log_tables= 0, lock_logger= 0; uint path_length; + uint log_type; DBUG_ENTER("mysql_truncate"); bzero((char*) &create_info,sizeof(create_info)); @@ -913,28 +919,16 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) DBUG_RETURN(TRUE); } + log_type= check_if_log_table(table_list->db_length, table_list->db, + table_list->table_name_length, + table_list->table_name, 1); /* close log tables in use */ - if (!my_strcasecmp(system_charset_info, table_list->db, "mysql")) + if (log_type) { - if (opt_log && - !my_strcasecmp(system_charset_info, table_list->table_name, - "general_log")) - { - lock_logger= 1; - logger.lock(); - logger.close_log_table(QUERY_LOG_GENERAL, FALSE); - closed_log_tables= closed_log_tables | QUERY_LOG_GENERAL; - } - else - if (opt_slow_log && - !my_strcasecmp(system_charset_info, table_list->table_name, - "slow_log")) - { - lock_logger= 1; - logger.lock(); - logger.close_log_table(QUERY_LOG_SLOW, FALSE); - closed_log_tables= closed_log_tables | QUERY_LOG_SLOW; - } + lock_logger= 1; + logger.lock(); + logger.close_log_table(log_type, FALSE); + closed_log_tables= closed_log_tables | log_type; } // Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this @@ -986,7 +980,7 @@ end: trunc_by_del: /* Probably InnoDB table */ - ulong save_options= thd->options; + ulonglong save_options= thd->options; table_list->lock_type= TL_WRITE; thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT); ha_enable_transaction(thd, FALSE); diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 5a9871c07c5..5712fbceac8 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -110,8 +109,6 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list) SELECT_LEX *first_select= unit->first_select(); TABLE *table= 0; select_union *derived_result; - bool is_union= first_select->next_select() && - first_select->next_select()->linkage == UNION_TYPE; /* prevent name resolving out of derived table */ for (SELECT_LEX *sl= first_select; sl; sl= sl->next_select()) diff --git a/sql/sql_do.cc b/sql/sql_do.cc index 98483ce2de6..a3eb93f87da 100644 --- a/sql/sql_do.cc +++ b/sql/sql_do.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_error.cc b/sql/sql_error.cc index dc7a437d7d1..70882d8f4e8 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_error.h b/sql/sql_error.h index f4a7b14ba1a..f98264dce50 100644 --- a/sql/sql_error.h +++ b/sql/sql_error.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 0d893a6c9be..91e61be0478 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -1,8 +1,7 @@ /* Copyright (C) 2000-2004 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -367,9 +366,9 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, strlen(tables->alias) + 1))) { table= hash_tables->table; - DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' tab %p", + DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' table: 0x%lx", hash_tables->db, hash_tables->table_name, - hash_tables->alias, table)); + hash_tables->alias, (long) table)); if (!table) { /* @@ -633,7 +632,8 @@ int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags, TABLE **table_ptr; bool did_lock= FALSE; DBUG_ENTER("mysql_ha_flush"); - DBUG_PRINT("enter", ("tables: %p mode_flags: 0x%02x", tables, mode_flags)); + DBUG_PRINT("enter", ("tables: 0x%lx mode_flags: 0x%02x", + (long) tables, mode_flags)); if (tables) { @@ -756,3 +756,41 @@ static int mysql_ha_flush_table(THD *thd, TABLE **table_ptr, uint mode_flags) DBUG_RETURN(0); } + + +/* + Mark tables for reopen. + + SYNOPSIS + mysql_ha_mark_tables_for_reopen() + thd Thread identifier. + table Table list to mark for reopen. + + DESCRIPTION + For each table found in the handler hash mark it as closed + (ready for reopen) and end all index/table scans. + + NOTE + The caller must lock LOCK_open. +*/ + +void mysql_ha_mark_tables_for_reopen(THD *thd, TABLE *table) +{ + DBUG_ENTER("mysql_ha_mark_tables_for_reopen"); + + safe_mutex_assert_owner(&LOCK_open); + for (; table; table= table->next) + { + TABLE_LIST *hash_tables; + if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash, + (byte*) table->alias, + strlen(table->alias) + 1))) + { + /* Mark table as ready for reopen. */ + hash_tables->table= NULL; + /* End open index/table scans. */ + table->file->ha_index_or_rnd_end(); + } + } + DBUG_VOID_RETURN; +} diff --git a/sql/sql_help.cc b/sql/sql_help.cc index 69d21f8b7bb..b8a904b0c6e 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -663,6 +662,8 @@ bool mysqld_help(THD *thd, const char *mask) Init tables and fields to be usable from items tables do not contain VIEWs => we can pass 0 as conds */ + thd->lex->select_lex.context.table_list= + thd->lex->select_lex.context.first_name_resolution_table= &tables[0]; if (setup_tables(thd, &thd->lex->select_lex.context, &thd->lex->select_lex.top_join_list, tables, &leaves, FALSE)) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 4dcbf9af4a0..479154cbed2 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -60,8 +59,8 @@ #include "sql_trigger.h" #include "sql_select.h" #include "sql_show.h" +#include "slave.h" -static int check_null_fields(THD *thd,TABLE *entry); #ifndef EMBEDDED_LIBRARY static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list); static int write_delayed(THD *thd, TABLE *table, enum_duplicates dup, @@ -82,6 +81,65 @@ static bool check_view_insertability(THD *thd, TABLE_LIST *view); #define my_safe_afree(ptr, size, min_length) if (size > min_length) my_free(ptr,MYF(0)) #endif +/* + Check that insert/update fields are from the same single table of a view. + + SYNOPSIS + check_view_single_update() + fields The insert/update fields to be checked. + view The view for insert. + map [in/out] The insert table map. + + DESCRIPTION + This function is called in 2 cases: + 1. to check insert fields. In this case *map will be set to 0. + Insert fields are checked to be all from the same single underlying + table of the given view. Otherwise the error is thrown. Found table + map is returned in the map parameter. + 2. to check update fields of the ON DUPLICATE KEY UPDATE clause. + In this case *map contains table_map found on the previous call of + the function to check insert fields. Update fields are checked to be + from the same table as the insert fields. + + RETURN + 0 OK + 1 Error +*/ + +bool check_view_single_update(List<Item> &fields, TABLE_LIST *view, + table_map *map) +{ + /* it is join view => we need to find the table for update */ + List_iterator_fast<Item> it(fields); + Item *item; + TABLE_LIST *tbl= 0; // reset for call to check_single_table() + table_map tables= 0; + + while ((item= it++)) + tables|= item->used_tables(); + + /* Check found map against provided map */ + if (*map) + { + if (tables != *map) + goto error; + return FALSE; + } + + if (view->check_single_table(&tbl, tables, view) || tbl == 0) + goto error; + + view->table= tbl->table; + *map= tables; + + return FALSE; + +error: + my_error(ER_VIEW_MULTIUPDATE, MYF(0), + view->view_db.str, view->view_name.str); + return TRUE; +} + /* Check if insert fields are correct. @@ -106,7 +164,7 @@ static bool check_view_insertability(THD *thd, TABLE_LIST *view); static int check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields, List<Item> &values, - bool check_unique) + bool check_unique, table_map *map) { TABLE *table= table_list->table; @@ -132,11 +190,11 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, #ifndef NO_EMBEDDED_ACCESS_CHECKS if (grant_option) { - Field_iterator_table fields; - fields.set_table(table); + Field_iterator_table field_it; + field_it.set_table(table); if (check_grant_all_columns(thd, INSERT_ACL, &table->grant, table->s->db.str, table->s->table_name.str, - &fields)) + &field_it)) return -1; } #endif @@ -184,21 +242,9 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, if (table_list->effective_algorithm == VIEW_ALGORITHM_MERGE) { - /* it is join view => we need to find table for update */ - List_iterator_fast<Item> it(fields); - Item *item; - TABLE_LIST *tbl= 0; // reset for call to check_single_table() - table_map map= 0; - - while ((item= it++)) - map|= item->used_tables(); - if (table_list->check_single_table(&tbl, map, table_list) || tbl == 0) - { - my_error(ER_VIEW_MULTIUPDATE, MYF(0), - table_list->view_db.str, table_list->view_name.str); + if (check_view_single_update(fields, table_list, map)) return -1; - } - table_list->table= table= tbl->table; + table= table_list->table; } if (check_unique && thd->dup_field) @@ -256,11 +302,13 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, */ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, - List<Item> &update_fields) + List<Item> &update_fields, table_map *map) { TABLE *table= insert_table_list->table; my_bool timestamp_mark; + LINT_INIT(timestamp_mark); + if (table->timestamp_field) { /* @@ -275,6 +323,10 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, if (setup_fields(thd, 0, update_fields, MARK_COLUMNS_WRITE, 0, 0)) return -1; + if (insert_table_list->effective_algorithm == VIEW_ALGORITHM_MERGE && + check_view_single_update(update_fields, insert_table_list, map)) + return -1; + if (table->timestamp_field) { /* Don't set timestamp column if this is modified. */ @@ -299,13 +351,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, bool ignore) { int error, res; - /* - log_on is about delayed inserts only. - By default, both logs are enabled (this won't cause problems if the server - runs without --log-update or --log-bin). - */ - bool log_on= ((thd->options & OPTION_BIN_LOG) || - (!(thd->security_ctx->master_access & SUPER_ACL))); bool transactional_table, joins_freed= FALSE; bool changed; uint value_count; @@ -320,6 +365,13 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, #ifndef EMBEDDED_LIBRARY char *query= thd->query; #endif + /* + log_on is about delayed inserts only. + By default, both logs are enabled (this won't cause problems if the server + runs without --log-update or --log-bin). + */ + bool log_on= ((thd->options & OPTION_BIN_LOG) || + (!(thd->security_ctx->master_access & SUPER_ACL))); thr_lock_type lock_type = table_list->lock_type; Item *unused_conds= 0; DBUG_ENTER("mysql_insert"); @@ -340,11 +392,42 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, (duplic == DUP_UPDATE)) lock_type=TL_WRITE; #endif + if ((lock_type == TL_WRITE_DELAYED) && + (global_system_variables.binlog_format == BINLOG_FORMAT_STMT) && + log_on && mysql_bin_log.is_open() && + (values_list.elements > 1)) + { + /* + Statement-based binary logging does not work in this case, because: + a) two concurrent statements may have their rows intermixed in the + queue, leading to autoincrement replication problems on slave (because + the values generated used for one statement don't depend only on the + value generated for the first row of this statement, so are not + replicable) + b) if first row of the statement has an error the full statement is + not binlogged, while next rows of the statement may be inserted. + c) if first row succeeds, statement is binlogged immediately with a + zero error code (i.e. "no error"), if then second row fails, query + will fail on slave too and slave will stop (wrongly believing that the + master got no error). + So we fallback to non-delayed INSERT. + Note that to be fully correct, we should test the "binlog format which + the delayed thread is going to use for this row". But in the common case + where the global binlog format is not changed and the session binlog + format may be changed, that is equal to the global binlog format. + We test it without mutex for speed reasons (condition rarely true), and + in the common case (global not changed) it is as good as without mutex; + if global value is changed, anyway there is uncertainty as the delayed + thread may be old and use the before-the-change value. + */ + lock_type= TL_WRITE; + } table_list->lock_type= lock_type; #ifndef EMBEDDED_LIBRARY if (lock_type == TL_WRITE_DELAYED) { + res= 1; if (thd->locked_tables) { DBUG_ASSERT(table_list->db); /* Must be set in the parser */ @@ -453,6 +536,14 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, thd->cuted_fields = 0L; table->next_number_field=table->found_next_number_field; +#ifdef HAVE_REPLICATION + if (thd->slave_thread && + (info.handle_duplicates == DUP_UPDATE) && + (table->next_number_field != NULL) && + rpl_master_has_bug(&active_mi->rli, 24432)) + goto abort; +#endif + error=0; thd->proc_info="update"; if (duplic != DUP_ERROR || ignore) @@ -739,7 +830,6 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view) Field_translator *trans_start= view->field_translation, *trans_end= trans_start + num; Field_translator *trans; - Field **field_ptr= table->field; uint used_fields_buff_size= bitmap_buffer_size(table->s->fields); uint32 *used_fields_buff= (uint32*)thd->alloc(used_fields_buff_size); MY_BITMAP used_fields; @@ -891,10 +981,13 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, Name_resolution_context_state ctx_state; bool insert_into_view= (table_list->view != 0); bool res= 0; + table_map map= 0; DBUG_ENTER("mysql_prepare_insert"); DBUG_PRINT("enter", ("table_list 0x%lx, table 0x%lx, view %d", (ulong)table_list, (ulong)table, (int)insert_into_view)); + /* INSERT should have a SELECT or VALUES clause */ + DBUG_ASSERT (!select_insert || !values); /* For subqueries in VALUES() we should not see the table in which we are @@ -926,44 +1019,40 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, if (mysql_prepare_insert_check_table(thd, table_list, fields, select_insert)) DBUG_RETURN(TRUE); - /* Save the state of the current name resolution context. */ - ctx_state.save_state(context, table_list); - - /* - Perform name resolution only in the first table - 'table_list', - which is the table that is inserted into. - */ - table_list->next_local= 0; - context->resolve_in_table_list_only(table_list); /* Prepare the fields in the statement. */ - if (values && - !(res= check_insert_fields(thd, context->table_list, fields, *values, - !insert_into_view) || - setup_fields(thd, 0, *values, MARK_COLUMNS_READ, 0, 0)) && - duplic == DUP_UPDATE) + if (values) { - select_lex->no_wrap_view_item= TRUE; - res= check_update_fields(thd, context->table_list, update_fields); - select_lex->no_wrap_view_item= FALSE; + /* if we have INSERT ... VALUES () we cannot have a GROUP BY clause */ + DBUG_ASSERT (!select_lex->group_list.elements); + + /* Save the state of the current name resolution context. */ + ctx_state.save_state(context, table_list); + /* - When we are not using GROUP BY we can refer to other tables in the - ON DUPLICATE KEY part. - */ - if (select_lex->group_list.elements == 0) + Perform name resolution only in the first table - 'table_list', + which is the table that is inserted into. + */ + table_list->next_local= 0; + context->resolve_in_table_list_only(table_list); + + if (!(res= check_insert_fields(thd, context->table_list, fields, *values, + !insert_into_view, &map) || + setup_fields(thd, 0, *values, MARK_COLUMNS_READ, 0, 0)) + && duplic == DUP_UPDATE) { - context->table_list->next_local= ctx_state.save_next_local; - /* first_name_resolution_table was set by resolve_in_table_list_only() */ - context->first_name_resolution_table-> - next_name_resolution_table= ctx_state.save_next_local; + select_lex->no_wrap_view_item= TRUE; + res= check_update_fields(thd, context->table_list, update_fields, &map); + select_lex->no_wrap_view_item= FALSE; } + + /* Restore the current context. */ + ctx_state.restore_state(context, table_list); + if (!res) res= setup_fields(thd, 0, update_values, MARK_COLUMNS_READ, 0, 0); } - /* Restore the current context. */ - ctx_state.restore_state(context, table_list); - if (res) DBUG_RETURN(res); @@ -974,7 +1063,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, { Item *fake_conds= 0; TABLE_LIST *duplicate; - if ((duplicate= unique_table(thd, table_list, table_list->next_global))) + if ((duplicate= unique_table(thd, table_list, table_list->next_global, 1))) { update_non_unique_table_error(table_list, "INSERT", duplicate); DBUG_RETURN(TRUE); @@ -1145,32 +1234,36 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) if (res == VIEW_CHECK_ERROR) goto before_trg_err; + table->file->restore_auto_increment(prev_insert_id); if ((error=table->file->ha_update_row(table->record[1], table->record[0]))) { if (info->ignore && !table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)) { - table->file->restore_auto_increment(prev_insert_id); goto ok_or_after_trg_err; } goto err; - } - info->updated++; - /* - If ON DUP KEY UPDATE updates a row instead of inserting one, it's - like a regular UPDATE statement: it should not affect the value of a - next SELECT LAST_INSERT_ID() or mysql_insert_id(). - Except if LAST_INSERT_ID(#) was in the INSERT query, which is - handled separately by THD::arg_of_last_insert_id_function. - */ - insert_id_for_cur_row= table->file->insert_id_for_cur_row= 0; - if (table->next_number_field) - table->file->adjust_next_insert_id_after_explicit_value(table->next_number_field->val_int()); - trg_error= (table->triggers && - table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, - TRG_ACTION_AFTER, TRUE)); - info->copied++; + } + if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) || + compare_record(table)) + { + info->updated++; + /* + If ON DUP KEY UPDATE updates a row instead of inserting one, it's + like a regular UPDATE statement: it should not affect the value of a + next SELECT LAST_INSERT_ID() or mysql_insert_id(). + Except if LAST_INSERT_ID(#) was in the INSERT query, which is + handled separately by THD::arg_of_last_insert_id_function. + */ + insert_id_for_cur_row= table->file->insert_id_for_cur_row= 0; + if (table->next_number_field) + table->file->adjust_next_insert_id_after_explicit_value(table->next_number_field->val_int()); + trg_error= (table->triggers && + table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, + TRG_ACTION_AFTER, TRUE)); + info->copied++; + } goto ok_or_after_trg_err; } else /* DUP_REPLACE */ @@ -1290,7 +1383,7 @@ int check_that_all_fields_are_given_values(THD *thd, TABLE *entry, { if (!bitmap_is_set(write_set, (*field)->field_index) && ((*field)->flags & NO_DEFAULT_VALUE_FLAG) && - ((*field)->real_type() != FIELD_TYPE_ENUM)) + ((*field)->real_type() != MYSQL_TYPE_ENUM)) { bool view= FALSE; if (table_list) @@ -1699,7 +1792,7 @@ static int write_delayed(THD *thd,TABLE *table, enum_duplicates duplic, LEX_STRING query, bool ignore, bool log_on) { - delayed_row *row; + delayed_row *row= 0; delayed_insert *di=thd->di; const Discrete_interval *forced_auto_inc; DBUG_ENTER("write_delayed"); @@ -1806,8 +1899,6 @@ void kill_delayed_threads(void) delayed_insert *tmp; while ((tmp=it++)) { - /* Ensure that the thread doesn't kill itself while we are looking at it */ - pthread_mutex_lock(&tmp->mutex); tmp->thd.killed= THD::KILL_CONNECTION; if (tmp->thd.mysys_var) { @@ -1826,7 +1917,6 @@ void kill_delayed_threads(void) } pthread_mutex_unlock(&tmp->thd.mysys_var->mutex); } - pthread_mutex_unlock(&tmp->mutex); } VOID(pthread_mutex_unlock(&LOCK_delayed_insert)); // For unlink from list } @@ -1844,7 +1934,7 @@ pthread_handler_t handle_delayed_insert(void *arg) pthread_detach_this_thread(); /* Add thread to THD list so that's it's visible in 'show processlist' */ pthread_mutex_lock(&LOCK_thread_count); - thd->thread_id=thread_id++; + thd->thread_id= thd->variables.pseudo_thread_id= thread_id++; thd->end_time(); threads.append(thd); thd->killed=abort_loop ? THD::KILL_CONNECTION : THD::NOT_KILLED; @@ -1875,14 +1965,8 @@ pthread_handler_t handle_delayed_insert(void *arg) strmov(thd->net.last_error,ER(thd->net.last_errno=ER_OUT_OF_RESOURCES)); goto err; } -#if !defined(__WIN__) && !defined(__NETWARE__) - sigset_t set; - VOID(sigemptyset(&set)); // Get mask in use - VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); -#endif /* open table */ - if (!(di->table=open_ltable(thd,&di->table_list,TL_WRITE_DELAYED))) { thd->fatal_error(); // Abort waiting inserts @@ -2040,7 +2124,9 @@ err: */ ha_rollback_stmt(thd); +#ifndef __WIN__ end: +#endif /* di should be unlinked from the thread handler list and have no active clients @@ -2277,7 +2363,6 @@ bool delayed_insert::handle_inserts(void) thd.proc_info=0; pthread_mutex_unlock(&mutex); -#ifdef HAVE_ROW_BASED_REPLICATION /* We need to flush the pending event when using row-based replication since the flushing normally done in binlog_query() is @@ -2292,7 +2377,6 @@ bool delayed_insert::handle_inserts(void) */ if (thd.current_stmt_binlog_row_based) thd.binlog_flush_pending_rows_event(TRUE); -#endif /* HAVE_ROW_BASED_REPLICATION */ if ((error=table->file->extra(HA_EXTRA_NO_CACHE))) { // This shouldn't happen @@ -2306,14 +2390,18 @@ bool delayed_insert::handle_inserts(void) DBUG_RETURN(0); err: - DBUG_EXECUTE("error", max_rows= 0;); +#ifndef DBUG_OFF + max_rows= 0; // For DBUG output +#endif /* Remove all not used rows */ while ((row=rows.get())) { delete row; thread_safe_increment(delayed_insert_errors,&LOCK_delayed_status); stacked_inserts--; - DBUG_EXECUTE("error", max_rows++;); +#ifndef DBUG_OFF + max_rows++; +#endif } DBUG_PRINT("error", ("dropped %lu rows after an error", max_rows)); thread_safe_increment(delayed_insert_errors, &LOCK_delayed_status); @@ -2402,6 +2490,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) { LEX *lex= thd->lex; int res; + table_map map= 0; SELECT_LEX *lex_current_select_save= lex->current_select; DBUG_ENTER("select_insert::prepare"); @@ -2414,12 +2503,11 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) */ lex->current_select= &lex->select_lex; res= check_insert_fields(thd, table_list, *fields, values, - !insert_into_view) || + !insert_into_view, &map) || setup_fields(thd, 0, values, MARK_COLUMNS_READ, 0, 0); if (info.handle_duplicates == DUP_UPDATE) { - /* Save the state of the current name resolution context. */ Name_resolution_context *context= &lex->select_lex.context; Name_resolution_context_state ctx_state; @@ -2432,21 +2520,43 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) lex->select_lex.no_wrap_view_item= TRUE; res= res || check_update_fields(thd, context->table_list, - *info.update_fields); + *info.update_fields, &map); lex->select_lex.no_wrap_view_item= FALSE; /* - When we are not using GROUP BY we can refer to other tables in the - ON DUPLICATE KEY part - */ - if (lex->select_lex.group_list.elements == 0) + When we are not using GROUP BY and there are no ungrouped aggregate functions + we can refer to other tables in the ON DUPLICATE KEY part. + We use next_name_resolution_table descructively, so check it first (views?) + */ + DBUG_ASSERT (!table_list->next_name_resolution_table); + if (lex->select_lex.group_list.elements == 0 && + !lex->select_lex.with_sum_func) + /* + We must make a single context out of the two separate name resolution contexts : + the INSERT table and the tables in the SELECT part of INSERT ... SELECT. + To do that we must concatenate the two lists + */ + table_list->next_name_resolution_table= + ctx_state.get_first_name_resolution_table(); + + res= res || setup_fields(thd, 0, *info.update_values, + MARK_COLUMNS_READ, 0, 0); + if (!res) { - context->table_list->next_local= ctx_state.save_next_local; - /* first_name_resolution_table was set by resolve_in_table_list_only() */ - context->first_name_resolution_table-> - next_name_resolution_table= ctx_state.save_next_local; + /* + Traverse the update values list and substitute fields from the + select for references (Item_ref objects) to them. This is done in + order to get correct values from those fields when the select + employs a temporary table. + */ + List_iterator<Item> li(*info.update_values); + Item *item; + + while ((item= li++)) + { + item->transform(&Item::update_value_transformer, + (byte*)lex->current_select); + } } - res= res || setup_fields(thd, 0, *info.update_values, MARK_COLUMNS_READ, - 0, 0); /* Restore the current context. */ ctx_state.restore_state(context, table_list); @@ -2466,7 +2576,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) query */ if (!(lex->current_select->options & OPTION_BUFFER_RESULT) && - unique_table(thd, table_list, table_list->next_global)) + unique_table(thd, table_list, table_list->next_global, 0)) { /* Using same table for INSERT and SELECT */ lex->current_select->options|= OPTION_BUFFER_RESULT; @@ -2487,6 +2597,15 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) } restore_record(table,s->default_values); // Get empty record table->next_number_field=table->found_next_number_field; + +#ifdef HAVE_REPLICATION + if (thd->slave_thread && + (info.handle_duplicates == DUP_UPDATE) && + (table->next_number_field != NULL) && + rpl_master_has_bug(&active_mi->rli, 24432)) + DBUG_RETURN(1); +#endif + thd->cuted_fields=0; if (info.ignore || info.handle_duplicates != DUP_ERROR) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); @@ -2640,8 +2759,7 @@ void select_insert::send_error(uint errcode,const char *err) If the creation of the table failed (due to a syntax error, for example), no table will have been opened and therefore 'table' will be NULL. In that case, we still need to execute the rollback - and the end of the function to truncate the binary log, but we can - skip all the intermediate steps. + and the end of the function. */ if (table) { @@ -2672,10 +2790,8 @@ void select_insert::send_error(uint errcode,const char *err) if (!table->file->has_transactions()) { if (mysql_bin_log.is_open()) - { thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length, table->file->has_transactions(), FALSE); - } if (!thd->current_stmt_binlog_row_based && !table->s->tmp_table && !can_rollback_data()) thd->options|= OPTION_STATUS_NO_TRANS_UPDATE; @@ -2692,7 +2808,7 @@ void select_insert::send_error(uint errcode,const char *err) bool select_insert::send_eof() { - int error,error2; + int error; bool const trans_table= table->file->has_transactions(); ulonglong id; DBUG_ENTER("select_insert::send_eof"); @@ -2744,9 +2860,9 @@ bool select_insert::send_eof() */ if (trans_table || thd->current_stmt_binlog_row_based) { - int const error2= ha_autocommit_or_rollback(thd, error); + int error2= ha_autocommit_or_rollback(thd, error); if (error2 && !error) - error=error2; + error= error2; } table->file->ha_release_auto_increment(); @@ -2942,7 +3058,24 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) DBUG_ENTER("select_create::prepare"); TABLEOP_HOOKS *hook_ptr= NULL; -#ifdef HAVE_ROW_BASED_REPLICATION + /* + For row-based replication, the CREATE-SELECT statement is written + in two pieces: the first one contain the CREATE TABLE statement + necessary to create the table and the second part contain the rows + that should go into the table. + + For non-temporary tables, the start of the CREATE-SELECT + implicitly commits the previous transaction, and all events + forming the statement will be stored the transaction cache. At end + of the statement, the entire statement is committed as a + transaction, and all events are written to the binary log. + + On the master, the table is locked for the duration of the + statement, but since the CREATE part is replicated as a simple + statement, there is no way to lock the table for accesses on the + slave. Hence, we have to hold on to the CREATE part of the + statement until the statement has finished. + */ class MY_HOOKS : public TABLEOP_HOOKS { public: MY_HOOKS(select_create *x) : ptr(x) { } @@ -2952,7 +3085,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) { TABLE const *const table = *tables; if (ptr->get_thd()->current_stmt_binlog_row_based && - table->s->tmp_table == NO_TMP_TABLE && + !table->s->tmp_table && !ptr->get_create_info()->table_existed) { ptr->binlog_show_create_table(tables, count); @@ -2964,22 +3097,19 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) MY_HOOKS hooks(this); hook_ptr= &hooks; -#endif unit= u; -#ifdef HAVE_ROW_BASED_REPLICATION /* - Start a statement transaction before the create if we are creating - a non-temporary table and are using row-based replication for the - statement. + Start a statement transaction before the create if we are using + row-based replication for the statement. If we are creating a + temporary table, we need to start a statement transaction. */ if ((thd->lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) == 0 && thd->current_stmt_binlog_row_based) { thd->binlog_start_trans_and_stmt(); } -#endif if (!(table= create_table_from_items(thd, create_info, create_table, extra_fields, keys, &values, @@ -3023,8 +3153,6 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) DBUG_RETURN(0); } - -#ifdef HAVE_ROW_BASED_REPLICATION void select_create::binlog_show_create_table(TABLE **tables, uint count) { @@ -3065,7 +3193,6 @@ select_create::binlog_show_create_table(TABLE **tables, uint count) /* is_trans */ TRUE, /* suppress_use */ FALSE); } -#endif // HAVE_ROW_BASED_REPLICATION void select_create::store_values(List<Item> &values) { @@ -3076,13 +3203,35 @@ void select_create::store_values(List<Item> &values) void select_create::send_error(uint errcode,const char *err) { + DBUG_ENTER("select_create::send_error"); + + DBUG_PRINT("info", + ("Current statement %s row-based", + thd->current_stmt_binlog_row_based ? "is" : "is NOT")); + DBUG_PRINT("info", + ("Current table (at 0x%lu) %s a temporary (or non-existant) table", + (ulong) table, + table && !table->s->tmp_table ? "is NOT" : "is")); + DBUG_PRINT("info", + ("Table %s prior to executing this statement", + get_create_info()->table_existed ? "existed" : "did not exist")); + /* - Disable binlog, because we "roll back" partial inserts in ::abort - by removing the table, even for non-transactional tables. + This will execute any rollbacks that are necessary before writing + the transcation cache. + + We disable the binary log since nothing should be written to the + binary log. This disabling is important, since we potentially do + a "roll back" of non-transactional tables by removing the table, + and the actual rollback might generate events that should not be + written to the binary log. + */ tmp_disable_binlog(thd); select_insert::send_error(errcode, err); reenable_binlog(thd); + + DBUG_VOID_RETURN; } @@ -3093,6 +3242,14 @@ bool select_create::send_eof() abort(); else { + /* + Do an implicit commit at end of statement for non-temporary + tables. This can fail, but we should unlock the table + nevertheless. + */ + if (!table->s->tmp_table) + ha_commit(thd); // Can fail, but we proceed anyway + table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); VOID(pthread_mutex_lock(&LOCK_open)); @@ -3111,12 +3268,31 @@ bool select_create::send_eof() void select_create::abort() { + DBUG_ENTER("select_create::abort"); VOID(pthread_mutex_lock(&LOCK_open)); + + /* + We roll back the statement, including truncating the transaction + cache of the binary log, if the statement failed. + + We roll back the statement prior to deleting the table and prior + to releasing the lock on the table, since there might be potential + for failure if the rollback is executed after the drop or after + unlocking the table. + + We also roll back the statement regardless of whether the creation + of the table succeeded or not, since we need to reset the binary + log state. + */ + if (thd->current_stmt_binlog_row_based) + ha_rollback_stmt(thd); + if (thd->extra_lock) { mysql_unlock_tables(thd, thd->extra_lock); thd->extra_lock=0; } + if (table) { table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); @@ -3128,17 +3304,8 @@ void select_create::abort() table->s->version= 0; hash_delete(&open_cache,(byte*) table); if (!create_info->table_existed) - { quick_rm_table(table_type, create_table->db, create_table->table_name, 0); - /* - We roll back the statement, including truncating the - transaction cache of the binary log, if the statement - failed. - */ - if (thd->current_stmt_binlog_row_based) - ha_rollback_stmt(thd); - } /* Tell threads waiting for refresh that something has happened */ if (version != refresh_version) broadcast_refresh(); @@ -3148,6 +3315,7 @@ void select_create::abort() table=0; // Safety } VOID(pthread_mutex_unlock(&LOCK_open)); + DBUG_VOID_RETURN; } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index dd358492d0d..389e7b5fd7a 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -101,6 +100,16 @@ void lex_free(void) } +void +st_parsing_options::reset() +{ + allows_variable= TRUE; + allows_select_into= TRUE; + allows_select_procedure= TRUE; + allows_derived= TRUE; +} + + /* This is called before every query that is to be parsed. Because of this, it's critical to not do too much things here. @@ -151,7 +160,7 @@ void lex_start(THD *thd, const uchar *buf, uint length) lex->safe_to_cache_query= 1; lex->time_zone_tables_used= 0; lex->leaf_tables_insert= 0; - lex->variables_used= 0; + lex->parsing_options.reset(); lex->empty_field_list_on_rset= 0; lex->select_lex.select_number= 1; lex->next_state=MY_LEX_START; @@ -177,12 +186,28 @@ void lex_start(THD *thd, const uchar *buf, uint length) lex->reset_query_tables_list(FALSE); lex->expr_allows_subselect= TRUE; - lex->name= 0; + lex->name.str= 0; + lex->name.length= 0; lex->event_parse_data= NULL; lex->nest_level=0 ; lex->allow_sum_func= 0; lex->in_sum_func= NULL; + /* + ok, there must be a better solution for this, long-term + I tried "bzero" in the sql_yacc.yy code, but that for + some reason made the values zero, even if they were set + */ + lex->server_options.server_name= 0; + lex->server_options.server_name_length= 0; + lex->server_options.host= 0; + lex->server_options.db= 0; + lex->server_options.username= 0; + lex->server_options.password= 0; + lex->server_options.scheme= 0; + lex->server_options.socket= 0; + lex->server_options.owner= 0; + lex->server_options.port= -1; DBUG_VOID_RETURN; } @@ -243,6 +268,12 @@ bool is_keyword(const char *name, uint len) return get_hash_symbol(name,len,0)!=0; } +bool is_lex_native_function(const LEX_STRING *name) +{ + DBUG_ASSERT(name != NULL); + return (get_hash_symbol(name->str, name->length, 1) != 0); +} + /* make a copy of token before ptr and set yytoklen */ static LEX_STRING get_token(LEX *lex,uint length) @@ -297,13 +328,15 @@ static char *get_text(LEX *lex) { c = yyGet(); #ifdef USE_MB - int l; - if (use_mb(cs) && - (l = my_ismbchar(cs, - (const char *)lex->ptr-1, - (const char *)lex->end_of_query))) { + { + int l; + if (use_mb(cs) && + (l = my_ismbchar(cs, + (const char *)lex->ptr-1, + (const char *)lex->end_of_query))) { lex->ptr += l-1; continue; + } } #endif if (c == '\\' && @@ -771,8 +804,8 @@ int MYSQLlex(void *arg, void *yythd) lex->tok_start=lex->ptr; // Skip first ` while ((c=yyGet())) { - int length; - if ((length= my_mbcharlen(cs, c)) == 1) + int var_length; + if ((var_length= my_mbcharlen(cs, c)) == 1) { if (c == quote_char) { @@ -784,9 +817,9 @@ int MYSQLlex(void *arg, void *yythd) } } #ifdef USE_MB - else if (length < 1) + else if (var_length < 1) break; // Error - lex->ptr+= length-1; + lex->ptr+= var_length-1; #endif } if (double_quotes) @@ -1150,6 +1183,7 @@ void st_select_lex::init_query() cond_count= between_count= with_wild= 0; conds_processed_with_permanent_arena= 0; ref_pointer_array= 0; + select_n_where_fields= 0; select_n_having_items= 0; subquery_in_having= explicit_limit= 0; is_item_list_lookup= 0; @@ -1173,7 +1207,6 @@ void st_select_lex::init_select() options= 0; sql_cache= SQL_CACHE_UNSPECIFIED; braces= 0; - when_list.empty(); expr_list.empty(); interval_list.empty(); use_index.empty(); @@ -1188,7 +1221,11 @@ void st_select_lex::init_select() select_limit= 0; /* denotes the default limit = HA_POS_ERROR */ offset_limit= 0; /* denotes the default offset = 0 */ with_sum_func= 0; - + is_correlated= 0; + cur_pos_in_select_list= UNDEF_POS; + non_agg_fields.empty(); + cond_value= having_value= Item::COND_UNDEF; + inner_refs_list.empty(); } /* @@ -1378,10 +1415,20 @@ void st_select_lex::mark_as_dependent(SELECT_LEX *last) if (!(s->uncacheable & UNCACHEABLE_DEPENDENT)) { // Select is dependent of outer select - s->uncacheable|= UNCACHEABLE_DEPENDENT; + s->uncacheable= (s->uncacheable & ~UNCACHEABLE_UNITED) | + UNCACHEABLE_DEPENDENT; SELECT_LEX_UNIT *munit= s->master_unit(); - munit->uncacheable|= UNCACHEABLE_DEPENDENT; + munit->uncacheable= (munit->uncacheable & ~UNCACHEABLE_UNITED) | + UNCACHEABLE_DEPENDENT; + for (SELECT_LEX *sl= munit->first_select(); sl ; sl= sl->next_select()) + { + if (sl != s && + !(sl->uncacheable & (UNCACHEABLE_DEPENDENT | UNCACHEABLE_UNITED))) + sl->uncacheable|= UNCACHEABLE_UNITED; + } } + is_correlated= TRUE; + this->master_unit()->item->is_correlated= TRUE; } bool st_select_lex_node::set_braces(bool value) { return 1; } @@ -1444,7 +1491,7 @@ bool st_select_lex::add_order_to_list(THD *thd, Item *item, bool asc) bool st_select_lex::add_item_to_list(THD *thd, Item *item) { DBUG_ENTER("st_select_lex::add_item_to_list"); - DBUG_PRINT("info", ("Item: %p", item)); + DBUG_PRINT("info", ("Item: 0x%lx", (long) item)); DBUG_RETURN(item_list.push_back(item)); } @@ -1536,6 +1583,7 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) (Item **)arena->alloc(sizeof(Item*) * (n_child_sum_items + item_list.elements + select_n_having_items + + select_n_where_fields + order_group_num)*5)) == 0; } @@ -1619,6 +1667,36 @@ void st_select_lex::print_limit(THD *thd, String *str) } } +/** + @brief Restore the LEX and THD in case of a parse error. + + This is a clean up call that is invoked by the Bison generated + parser before returning an error from MYSQLparse. If your + semantic actions manipulate with the global thread state (which + is a very bad practice and should not normally be employed) and + need a clean-up in case of error, and you can not use %destructor + rule in the grammar file itself, this function should be used + to implement the clean up. +*/ + +void st_lex::cleanup_lex_after_parse_error(THD *thd) +{ + /* + Delete sphead for the side effect of restoring of the original + LEX state, thd->lex, thd->mem_root and thd->free_list if they + were replaced when parsing stored procedure statements. We + will never use sphead object after a parse error, so it's okay + to delete it only for the sake of the side effect. + TODO: make this functionality explicit in sp_head class. + Sic: we must nullify the member of the main lex, not the + current one that will be thrown away + */ + if (thd->lex->sphead) + { + delete thd->lex->sphead; + thd->lex->sphead= NULL; + } +} /* Initialize (or reset) Query_tables_list object. @@ -1644,15 +1722,22 @@ void Query_tables_list::reset_query_tables_list(bool init) query_tables_last= &query_tables; query_tables_own_last= 0; if (init) - hash_init(&sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0); + { + /* + We delay real initialization of hash (and therefore related + memory allocation) until first insertion into this hash. + */ + hash_clear(&sroutines); + } else if (sroutines.records) + { + /* Non-zero sroutines.records means that hash was initialized. */ my_hash_reset(&sroutines); + } sroutines_list.empty(); sroutines_list_own_last= sroutines_list.next; sroutines_list_own_elements= 0; -#ifdef HAVE_ROW_BASED_REPLICATION binlog_row_based_if_mixed= FALSE; -#endif } @@ -1717,13 +1802,14 @@ bool st_lex::can_be_merged() bool selects_allow_merge= select_lex.next_select() == 0; if (selects_allow_merge) { - for (SELECT_LEX_UNIT *unit= select_lex.first_inner_unit(); - unit; - unit= unit->next_unit()) + for (SELECT_LEX_UNIT *tmp_unit= select_lex.first_inner_unit(); + tmp_unit; + tmp_unit= tmp_unit->next_unit()) { - if (unit->first_select()->parent_lex == this && - (unit->item == 0 || - (unit->item->place() != IN_WHERE && unit->item->place() != IN_ON))) + if (tmp_unit->first_select()->parent_lex == this && + (tmp_unit->item == 0 || + (tmp_unit->item->place() != IN_WHERE && + tmp_unit->item->place() != IN_ON))) { selects_allow_merge= 0; break; @@ -1732,7 +1818,6 @@ bool st_lex::can_be_merged() } return (selects_allow_merge && - select_lex.order_list.elements == 0 && select_lex.group_list.elements == 0 && select_lex.having == 0 && select_lex.with_sum_func == 0 && @@ -2021,12 +2106,12 @@ void st_lex::first_lists_tables_same() FALSE - success */ -bool st_lex::add_time_zone_tables_to_query_tables(THD *thd) +bool st_lex::add_time_zone_tables_to_query_tables(THD *thd_arg) { /* We should not add these tables twice */ if (!time_zone_tables_used) { - time_zone_tables_used= my_tz_get_table_list(thd, &query_tables_last); + time_zone_tables_used= my_tz_get_table_list(thd_arg, &query_tables_last); if (time_zone_tables_used == &fake_time_zone_tables_list) return TRUE; } @@ -2142,6 +2227,28 @@ void st_lex::restore_backup_query_tables_list(Query_tables_list *backup) /* + Checks for usage of routines and/or tables in a parsed statement + + SYNOPSIS + st_lex:table_or_sp_used() + + RETURN + FALSE No routines and tables used + TRUE Either or both routines and tables are used. +*/ + +bool st_lex::table_or_sp_used() +{ + DBUG_ENTER("table_or_sp_used"); + + if (sroutines.records || query_tables) + DBUG_RETURN(TRUE); + + DBUG_RETURN(FALSE); +} + + +/* Do end-of-prepare fixup for list of tables and their merge-VIEWed tables SYNOPSIS @@ -2208,6 +2315,7 @@ void st_select_lex::fix_prepare_information(THD *thd, Item **conds, } } + /* There are st_select_lex::add_table_to_list & st_select_lex::set_lock_for_tables are in sql_parse.cc diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 1815774526f..1271df5e458 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -111,6 +110,7 @@ enum enum_sql_command { SQLCOM_SHOW_AUTHORS, SQLCOM_BINLOG_BASE64_EVENT, SQLCOM_SHOW_PLUGINS, SQLCOM_SHOW_CONTRIBUTORS, + SQLCOM_CREATE_SERVER, SQLCOM_DROP_SERVER, SQLCOM_ALTER_SERVER, SQLCOM_CREATE_EVENT, SQLCOM_ALTER_EVENT, SQLCOM_DROP_EVENT, SQLCOM_SHOW_CREATE_EVENT, SQLCOM_SHOW_EVENTS, @@ -174,6 +174,14 @@ enum enum_drop_mode typedef List<Item> List_item; +/* SERVERS CACHE CHANGES */ +typedef struct st_lex_server_options +{ + long port; + uint server_name_length; + char *server_name, *host, *db, *username, *password, *scheme, *socket, *owner; +} LEX_SERVER_OPTIONS; + typedef struct st_lex_master_info { char *host, *user, *password, *log_file_name; @@ -423,8 +431,9 @@ protected: TABLE *table; /* temporary table using for appending UNION results */ select_result *result; - ulong found_rows_for_union; - bool res; + ulonglong found_rows_for_union; + bool saved_error; + public: bool prepared, // prepare phase already performed for UNION (unit) optimized, // optimize phase already performed for UNION (unit) @@ -514,6 +523,8 @@ public: Item *where, *having; /* WHERE & HAVING clauses */ Item *prep_where; /* saved WHERE clause for prepared statement processing */ Item *prep_having;/* saved HAVING clause for prepared statement processing */ + /* Saved values of the WHERE and HAVING clauses*/ + Item::cond_result cond_value, having_value; /* point on lex in which it was created, used in view subquery detection */ st_lex *parent_lex; enum olap_type olap; @@ -544,7 +555,6 @@ public: SQL_LIST order_list; /* ORDER clause */ List<List_item> expr_list; - List<List_item> when_list; /* WHEN clause (expression) */ SQL_LIST *gorder_list; Item *select_limit, *offset_limit; /* LIMIT clause parameters */ // Arrays of pointers to top elements of all_fields list @@ -558,6 +568,11 @@ public: uint select_n_having_items; uint cond_count; /* number of arguments of and/or/xor in where/having/on */ uint between_count; /* number of between predicates in where/having/on */ + /* + Number of fields used in select list or where clause of current select + and all inner subselects. + */ + uint select_n_where_fields; enum_parsing_place parsing_place; /* where we are parsing expression */ bool with_sum_func; /* sum function indicator */ /* @@ -575,7 +590,8 @@ public: bool braces; /* SELECT ... UNION (SELECT ... ) <- this braces */ /* TRUE when having fix field called in processing of this SELECT */ bool having_fix_field; - + /* List of references to fields referenced from inner selects */ + List<Item_outer_ref> inner_refs_list; /* Number of Item_sum-derived objects in this SELECT */ uint n_sum_items; /* Number of Item_sum-derived objects in children and descendant SELECTs */ @@ -588,6 +604,8 @@ public: query processing end even if we use temporary table */ bool subquery_in_having; + /* TRUE <=> this SELECT is correlated w.r.t. some ancestor select */ + bool is_correlated; /* This variable is required to ensure proper work of subqueries and stored procedures. Generally, one should use the states of @@ -606,7 +624,26 @@ public: bool no_wrap_view_item; /* exclude this select from check of unique_table() */ bool exclude_from_table_unique_test; + /* List of fields that aren't under an aggregate function */ + List<Item_field> non_agg_fields; + /* index in the select list of the expression currently being fixed */ + int cur_pos_in_select_list; + List<udf_func> udf_list; /* udf function calls stack */ + /* + This is a copy of the original JOIN USING list that comes from + the parser. The parser : + 1. Sets the natural_join of the second TABLE_LIST in the join + and the st_select_lex::prev_join_using. + 2. Makes a parent TABLE_LIST and sets its is_natural_join/ + join_using_fields members. + 3. Uses the wrapper TABLE_LIST as a table in the upper level. + We cannot assign directly to join_using_fields in the parser because + at stage (1.) the parent TABLE_LIST is not constructed yet and + the assignment will override the JOIN USING fields of the lower level + joins on the right. + */ + List<String> *prev_join_using; void init_query(); void init_select(); st_select_lex_unit* master_unit(); @@ -792,7 +829,11 @@ public: 0 - indicates that this query does not need prelocking. */ TABLE_LIST **query_tables_own_last; - /* Set of stored routines called by statement. */ + /* + Set of stored routines called by statement. + (Note that we use lazy-initialization for this hash). + */ + enum { START_SROUTINES_HASH_SIZE= 16 }; HASH sroutines; /* List linking elements of 'sroutines' set. Allows you to add new elements @@ -807,7 +848,6 @@ public: byte **sroutines_list_own_last; uint sroutines_list_own_elements; -#ifdef HAVE_ROW_BASED_REPLICATION /* Tells if the parsing stage detected that some items require row-based binlogging to give a reliable binlog/replication, or if we will use @@ -815,7 +855,6 @@ public: binlogging. */ bool binlog_row_based_if_mixed; -#endif /* These constructor and destructor serve for creation/destruction @@ -867,6 +906,23 @@ public: }; +/* + st_parsing_options contains the flags for constructions that are + allowed in the current statement. +*/ + +struct st_parsing_options +{ + bool allows_variable; + bool allows_select_into; + bool allows_select_procedure; + bool allows_derived; + + st_parsing_options() { reset(); } + void reset(); +}; + + /* The state of the lex parsing. This is saved in the THD struct */ typedef struct st_lex : public Query_tables_list @@ -885,7 +941,8 @@ typedef struct st_lex : public Query_tables_list /* The values of tok_start/tok_end as they were one call of MYSQLlex before */ const uchar *tok_start_prev, *tok_end_prev; - char *length,*dec,*change,*name; + char *length,*dec,*change; + LEX_STRING name; Table_ident *like_name; char *help_arg; char *backup_dir; /* For RESTORE/BACKUP */ @@ -951,6 +1008,7 @@ typedef struct st_lex : public Query_tables_list HA_CREATE_INFO create_info; KEY_CREATE_INFO key_create_info; LEX_MASTER_INFO mi; // used by CHANGE MASTER + LEX_SERVER_OPTIONS server_options; USER_RESOURCES mqh; ulong type; /* @@ -1023,7 +1081,7 @@ typedef struct st_lex : public Query_tables_list bool stmt_prepare_mode; bool safe_to_cache_query; bool subqueries, ignore; - bool variables_used; + st_parsing_options parsing_options; ALTER_INFO alter_info; /* Prepared statements SQL syntax:*/ LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */ @@ -1177,9 +1235,15 @@ typedef struct st_lex : public Query_tables_list { return context_stack.head(); } + /* + Restore the LEX and THD in case of a parse error. + */ + static void cleanup_lex_after_parse_error(THD *thd); void reset_n_backup_query_tables_list(Query_tables_list *backup); void restore_backup_query_tables_list(Query_tables_list *backup); + + bool table_or_sp_used(); } LEX; struct st_lex_local: public st_lex @@ -1205,4 +1269,6 @@ extern void lex_end(LEX *lex); extern int MYSQLlex(void *arg, void *yythd); extern const uchar *skip_rear_comments(const uchar *ubegin, const uchar *uend); +extern bool is_lex_native_function(const LEX_STRING *name); + #endif /* MYSQL_SERVER */ diff --git a/sql/sql_list.cc b/sql/sql_list.cc index d57a7dfe4e3..01ab9b91424 100644 --- a/sql/sql_list.cc +++ b/sql/sql_list.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2001, 2003, 2005 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_list.h b/sql/sql_list.h index 070fd1e3d9a..af30cbe0d6a 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -39,6 +38,8 @@ public: static void operator delete(void *ptr, size_t size) { TRASH(ptr, size); } static void operator delete(void *ptr, MEM_ROOT *mem_root) { /* never called */ } + static void operator delete[](void *ptr, MEM_ROOT *mem_root) + { /* never called */ } static void operator delete[](void *ptr, size_t size) { TRASH(ptr, size); } #ifdef HAVE_purify bool dummy; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index b85610eaa6f..cf356a4b336 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -83,10 +82,11 @@ static int read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, List<Item> &set_values, READ_INFO &read_info, String &enclosed, ulong skip_lines, bool ignore_check_option_errors); +#ifndef EMBEDDED_LIBRARY static bool write_execute_load_query_log_event(THD *thd, bool duplicates, bool ignore, bool transactional_table); - +#endif /* EMBEDDED_LIBRARY */ /* Execute LOAD DATA query @@ -175,7 +175,7 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, table is marked to be 'used for insert' in which case we should never mark this table as 'const table' (ie, one that has only one row). */ - if (unique_table(thd, table_list, table_list->next_global)) + if (unique_table(thd, table_list, table_list->next_global, 0)) { my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name); DBUG_RETURN(TRUE); @@ -305,6 +305,15 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if ((stat_info.st_mode & S_IFIFO) == S_IFIFO) is_fifo = 1; #endif + + if (opt_secure_file_priv && + strncmp(opt_secure_file_priv, name, strlen(opt_secure_file_priv))) + { + /* Read only allowed from within dir specified by secure_file_priv */ + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--secure-file-priv"); + DBUG_RETURN(TRUE); + } + } if ((file=my_open(name,O_RDONLY,MYF(MY_WME))) < 0) DBUG_RETURN(TRUE); @@ -316,7 +325,8 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, info.handle_duplicates=handle_duplicates; info.escape_char=escaped->length() ? (*escaped)[0] : INT_MAX; - READ_INFO read_info(file,tot_length,thd->variables.collation_database, + READ_INFO read_info(file,tot_length, + ex->cs ? ex->cs : thd->variables.collation_database, *field_term,*ex->line_start, *ex->line_term, *enclosed, info.escape_char, read_file_from_client, is_fifo); if (read_info.error) @@ -458,14 +468,12 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, } sprintf(name, ER(ER_LOAD_INFO), (ulong) info.records, (ulong) info.deleted, (ulong) (info.records - info.copied), (ulong) thd->cuted_fields); - send_ok(thd,info.copied+info.deleted,0L,name); if (!transactional_table) thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; #ifndef EMBEDDED_LIBRARY if (mysql_bin_log.is_open()) { -#ifdef HAVE_ROW_BASED_REPLICATION /* We need to do the job that is normally done inside binlog_query() here, which is to ensure that the pending event @@ -477,7 +485,6 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if (thd->current_stmt_binlog_row_based) thd->binlog_flush_pending_rows_event(true); else -#endif { /* As already explained above, we need to call end_io_cache() or the last @@ -496,6 +503,8 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if (transactional_table) error=ha_autocommit_or_rollback(thd,error); + /* ok to client sent only after binlog write and engine commit */ + send_ok(thd, info.copied + info.deleted, 0L, name); err: table->file->ha_release_auto_increment(); if (thd->lock) @@ -508,6 +517,8 @@ err: } +#ifndef EMBEDDED_LIBRARY + /* Not a very useful function; just to avoid duplication of code */ static bool write_execute_load_query_log_event(THD *thd, bool duplicates, bool ignore, @@ -524,6 +535,7 @@ static bool write_execute_load_query_log_event(THD *thd, return mysql_bin_log.write(&e); } +#endif /**************************************************************************** ** Read of rows of fixed size + optional garage + optonal newline @@ -710,13 +722,18 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, if (item->type() == Item::FIELD_ITEM) { Field *field= ((Item_field *)item)->field; - field->reset(); + if (field->reset()) + { + my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0), field->field_name, + thd->row_count); + DBUG_RETURN(1); + } field->set_null(); if (field == table->next_number_field) table->auto_increment_field_not_null= TRUE; if (!field->maybe_null()) { - if (field->type() == FIELD_TYPE_TIMESTAMP) + if (field->type() == MYSQL_TYPE_TIMESTAMP) ((Field_timestamp*) field)->set_time(); else if (field != table->next_number_field) field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, @@ -759,6 +776,13 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, { if (item->type() == Item::FIELD_ITEM) { + Field *field= ((Item_field *)item)->field; + if (field->reset()) + { + my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0),field->field_name, + thd->row_count); + DBUG_RETURN(1); + } /* QQ: We probably should not throw warning for each field. But how about intention to always have the same number diff --git a/sql/sql_locale.cc b/sql/sql_locale.cc index b947b9dfa98..4e61c664106 100644 --- a/sql/sql_locale.cc +++ b/sql/sql_locale.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -24,17 +23,6 @@ #include "mysql_priv.h" -MY_LOCALE *my_locale_by_name(const char *name) -{ - MY_LOCALE **locale; - for( locale= my_locales; *locale != NULL; locale++) - { - if(!strcmp((*locale)->name, name)) - return *locale; - } - return NULL; -} - /***** LOCALE BEGIN ar_AE: Arabic - United Arab Emirates *****/ static const char *my_locale_month_names_ar_AE[13] = {"يناير","Ùبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوÙمبر","ديسمبر", NullS }; @@ -52,7 +40,17 @@ static TYPELIB my_locale_typelib_day_names_ar_AE = { array_elements(my_locale_day_names_ar_AE)-1, "", my_locale_day_names_ar_AE, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ar_AE = { array_elements(my_locale_ab_day_names_ar_AE)-1, "", my_locale_ab_day_names_ar_AE, NULL }; -MY_LOCALE my_locale_ar_AE ( "ar_AE", "Arabic - United Arab Emirates", FALSE, &my_locale_typelib_month_names_ar_AE, &my_locale_typelib_ab_month_names_ar_AE, &my_locale_typelib_day_names_ar_AE, &my_locale_typelib_ab_day_names_ar_AE ); +MY_LOCALE my_locale_ar_AE +( + 6, + "ar_AE", + "Arabic - United Arab Emirates", + FALSE, + &my_locale_typelib_month_names_ar_AE, + &my_locale_typelib_ab_month_names_ar_AE, + &my_locale_typelib_day_names_ar_AE, + &my_locale_typelib_ab_day_names_ar_AE +); /***** LOCALE END ar_AE *****/ /***** LOCALE BEGIN ar_BH: Arabic - Bahrain *****/ @@ -72,7 +70,17 @@ static TYPELIB my_locale_typelib_day_names_ar_BH = { array_elements(my_locale_day_names_ar_BH)-1, "", my_locale_day_names_ar_BH, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ar_BH = { array_elements(my_locale_ab_day_names_ar_BH)-1, "", my_locale_ab_day_names_ar_BH, NULL }; -MY_LOCALE my_locale_ar_BH ( "ar_BH", "Arabic - Bahrain", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); +MY_LOCALE my_locale_ar_BH +( + 7, + "ar_BH", + "Arabic - Bahrain", + FALSE, + &my_locale_typelib_month_names_ar_BH, + &my_locale_typelib_ab_month_names_ar_BH, + &my_locale_typelib_day_names_ar_BH, + &my_locale_typelib_ab_day_names_ar_BH +); /***** LOCALE END ar_BH *****/ /***** LOCALE BEGIN ar_JO: Arabic - Jordan *****/ @@ -92,7 +100,17 @@ static TYPELIB my_locale_typelib_day_names_ar_JO = { array_elements(my_locale_day_names_ar_JO)-1, "", my_locale_day_names_ar_JO, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ar_JO = { array_elements(my_locale_ab_day_names_ar_JO)-1, "", my_locale_ab_day_names_ar_JO, NULL }; -MY_LOCALE my_locale_ar_JO ( "ar_JO", "Arabic - Jordan", FALSE, &my_locale_typelib_month_names_ar_JO, &my_locale_typelib_ab_month_names_ar_JO, &my_locale_typelib_day_names_ar_JO, &my_locale_typelib_ab_day_names_ar_JO ); +MY_LOCALE my_locale_ar_JO +( + 8, + "ar_JO", + "Arabic - Jordan", + FALSE, + &my_locale_typelib_month_names_ar_JO, + &my_locale_typelib_ab_month_names_ar_JO, + &my_locale_typelib_day_names_ar_JO, + &my_locale_typelib_ab_day_names_ar_JO +); /***** LOCALE END ar_JO *****/ /***** LOCALE BEGIN ar_SA: Arabic - Saudi Arabia *****/ @@ -112,7 +130,17 @@ static TYPELIB my_locale_typelib_day_names_ar_SA = { array_elements(my_locale_day_names_ar_SA)-1, "", my_locale_day_names_ar_SA, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ar_SA = { array_elements(my_locale_ab_day_names_ar_SA)-1, "", my_locale_ab_day_names_ar_SA, NULL }; -MY_LOCALE my_locale_ar_SA ( "ar_SA", "Arabic - Saudi Arabia", FALSE, &my_locale_typelib_month_names_ar_SA, &my_locale_typelib_ab_month_names_ar_SA, &my_locale_typelib_day_names_ar_SA, &my_locale_typelib_ab_day_names_ar_SA ); +MY_LOCALE my_locale_ar_SA +( + 9, + "ar_SA", + "Arabic - Saudi Arabia", + FALSE, + &my_locale_typelib_month_names_ar_SA, + &my_locale_typelib_ab_month_names_ar_SA, + &my_locale_typelib_day_names_ar_SA, + &my_locale_typelib_ab_day_names_ar_SA +); /***** LOCALE END ar_SA *****/ /***** LOCALE BEGIN ar_SY: Arabic - Syria *****/ @@ -132,7 +160,17 @@ static TYPELIB my_locale_typelib_day_names_ar_SY = { array_elements(my_locale_day_names_ar_SY)-1, "", my_locale_day_names_ar_SY, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ar_SY = { array_elements(my_locale_ab_day_names_ar_SY)-1, "", my_locale_ab_day_names_ar_SY, NULL }; -MY_LOCALE my_locale_ar_SY ( "ar_SY", "Arabic - Syria", FALSE, &my_locale_typelib_month_names_ar_SY, &my_locale_typelib_ab_month_names_ar_SY, &my_locale_typelib_day_names_ar_SY, &my_locale_typelib_ab_day_names_ar_SY ); +MY_LOCALE my_locale_ar_SY +( + 10, + "ar_SY", + "Arabic - Syria", + FALSE, + &my_locale_typelib_month_names_ar_SY, + &my_locale_typelib_ab_month_names_ar_SY, + &my_locale_typelib_day_names_ar_SY, + &my_locale_typelib_ab_day_names_ar_SY +); /***** LOCALE END ar_SY *****/ /***** LOCALE BEGIN be_BY: Belarusian - Belarus *****/ @@ -152,7 +190,17 @@ static TYPELIB my_locale_typelib_day_names_be_BY = { array_elements(my_locale_day_names_be_BY)-1, "", my_locale_day_names_be_BY, NULL }; static TYPELIB my_locale_typelib_ab_day_names_be_BY = { array_elements(my_locale_ab_day_names_be_BY)-1, "", my_locale_ab_day_names_be_BY, NULL }; -MY_LOCALE my_locale_be_BY ( "be_BY", "Belarusian - Belarus", FALSE, &my_locale_typelib_month_names_be_BY, &my_locale_typelib_ab_month_names_be_BY, &my_locale_typelib_day_names_be_BY, &my_locale_typelib_ab_day_names_be_BY ); +MY_LOCALE my_locale_be_BY +( + 11, + "be_BY", + "Belarusian - Belarus", + FALSE, + &my_locale_typelib_month_names_be_BY, + &my_locale_typelib_ab_month_names_be_BY, + &my_locale_typelib_day_names_be_BY, + &my_locale_typelib_ab_day_names_be_BY +); /***** LOCALE END be_BY *****/ /***** LOCALE BEGIN bg_BG: Bulgarian - Bulgaria *****/ @@ -172,7 +220,17 @@ static TYPELIB my_locale_typelib_day_names_bg_BG = { array_elements(my_locale_day_names_bg_BG)-1, "", my_locale_day_names_bg_BG, NULL }; static TYPELIB my_locale_typelib_ab_day_names_bg_BG = { array_elements(my_locale_ab_day_names_bg_BG)-1, "", my_locale_ab_day_names_bg_BG, NULL }; -MY_LOCALE my_locale_bg_BG ( "bg_BG", "Bulgarian - Bulgaria", FALSE, &my_locale_typelib_month_names_bg_BG, &my_locale_typelib_ab_month_names_bg_BG, &my_locale_typelib_day_names_bg_BG, &my_locale_typelib_ab_day_names_bg_BG ); +MY_LOCALE my_locale_bg_BG +( + 12, + "bg_BG", + "Bulgarian - Bulgaria", + FALSE, + &my_locale_typelib_month_names_bg_BG, + &my_locale_typelib_ab_month_names_bg_BG, + &my_locale_typelib_day_names_bg_BG, + &my_locale_typelib_ab_day_names_bg_BG +); /***** LOCALE END bg_BG *****/ /***** LOCALE BEGIN ca_ES: Catalan - Catalan *****/ @@ -192,7 +250,17 @@ static TYPELIB my_locale_typelib_day_names_ca_ES = { array_elements(my_locale_day_names_ca_ES)-1, "", my_locale_day_names_ca_ES, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ca_ES = { array_elements(my_locale_ab_day_names_ca_ES)-1, "", my_locale_ab_day_names_ca_ES, NULL }; -MY_LOCALE my_locale_ca_ES ( "ca_ES", "Catalan - Catalan", FALSE, &my_locale_typelib_month_names_ca_ES, &my_locale_typelib_ab_month_names_ca_ES, &my_locale_typelib_day_names_ca_ES, &my_locale_typelib_ab_day_names_ca_ES ); +MY_LOCALE my_locale_ca_ES +( + 13, + "ca_ES", + "Catalan - Catalan", + FALSE, + &my_locale_typelib_month_names_ca_ES, + &my_locale_typelib_ab_month_names_ca_ES, + &my_locale_typelib_day_names_ca_ES, + &my_locale_typelib_ab_day_names_ca_ES +); /***** LOCALE END ca_ES *****/ /***** LOCALE BEGIN cs_CZ: Czech - Czech Republic *****/ @@ -212,7 +280,17 @@ static TYPELIB my_locale_typelib_day_names_cs_CZ = { array_elements(my_locale_day_names_cs_CZ)-1, "", my_locale_day_names_cs_CZ, NULL }; static TYPELIB my_locale_typelib_ab_day_names_cs_CZ = { array_elements(my_locale_ab_day_names_cs_CZ)-1, "", my_locale_ab_day_names_cs_CZ, NULL }; -MY_LOCALE my_locale_cs_CZ ( "cs_CZ", "Czech - Czech Republic", FALSE, &my_locale_typelib_month_names_cs_CZ, &my_locale_typelib_ab_month_names_cs_CZ, &my_locale_typelib_day_names_cs_CZ, &my_locale_typelib_ab_day_names_cs_CZ ); +MY_LOCALE my_locale_cs_CZ +( + 14, + "cs_CZ", + "Czech - Czech Republic", + FALSE, + &my_locale_typelib_month_names_cs_CZ, + &my_locale_typelib_ab_month_names_cs_CZ, + &my_locale_typelib_day_names_cs_CZ, + &my_locale_typelib_ab_day_names_cs_CZ +); /***** LOCALE END cs_CZ *****/ /***** LOCALE BEGIN da_DK: Danish - Denmark *****/ @@ -232,7 +310,17 @@ static TYPELIB my_locale_typelib_day_names_da_DK = { array_elements(my_locale_day_names_da_DK)-1, "", my_locale_day_names_da_DK, NULL }; static TYPELIB my_locale_typelib_ab_day_names_da_DK = { array_elements(my_locale_ab_day_names_da_DK)-1, "", my_locale_ab_day_names_da_DK, NULL }; -MY_LOCALE my_locale_da_DK ( "da_DK", "Danish - Denmark", FALSE, &my_locale_typelib_month_names_da_DK, &my_locale_typelib_ab_month_names_da_DK, &my_locale_typelib_day_names_da_DK, &my_locale_typelib_ab_day_names_da_DK ); +MY_LOCALE my_locale_da_DK +( + 15, + "da_DK", + "Danish - Denmark", + FALSE, + &my_locale_typelib_month_names_da_DK, + &my_locale_typelib_ab_month_names_da_DK, + &my_locale_typelib_day_names_da_DK, + &my_locale_typelib_ab_day_names_da_DK +); /***** LOCALE END da_DK *****/ /***** LOCALE BEGIN de_AT: German - Austria *****/ @@ -252,7 +340,17 @@ static TYPELIB my_locale_typelib_day_names_de_AT = { array_elements(my_locale_day_names_de_AT)-1, "", my_locale_day_names_de_AT, NULL }; static TYPELIB my_locale_typelib_ab_day_names_de_AT = { array_elements(my_locale_ab_day_names_de_AT)-1, "", my_locale_ab_day_names_de_AT, NULL }; -MY_LOCALE my_locale_de_AT ( "de_AT", "German - Austria", FALSE, &my_locale_typelib_month_names_de_AT, &my_locale_typelib_ab_month_names_de_AT, &my_locale_typelib_day_names_de_AT, &my_locale_typelib_ab_day_names_de_AT ); +MY_LOCALE my_locale_de_AT +( + 16, + "de_AT", + "German - Austria", + FALSE, + &my_locale_typelib_month_names_de_AT, + &my_locale_typelib_ab_month_names_de_AT, + &my_locale_typelib_day_names_de_AT, + &my_locale_typelib_ab_day_names_de_AT +); /***** LOCALE END de_AT *****/ /***** LOCALE BEGIN de_DE: German - Germany *****/ @@ -272,7 +370,17 @@ static TYPELIB my_locale_typelib_day_names_de_DE = { array_elements(my_locale_day_names_de_DE)-1, "", my_locale_day_names_de_DE, NULL }; static TYPELIB my_locale_typelib_ab_day_names_de_DE = { array_elements(my_locale_ab_day_names_de_DE)-1, "", my_locale_ab_day_names_de_DE, NULL }; -MY_LOCALE my_locale_de_DE ( "de_DE", "German - Germany", FALSE, &my_locale_typelib_month_names_de_DE, &my_locale_typelib_ab_month_names_de_DE, &my_locale_typelib_day_names_de_DE, &my_locale_typelib_ab_day_names_de_DE ); +MY_LOCALE my_locale_de_DE +( + 4, + "de_DE", + "German - Germany", + FALSE, + &my_locale_typelib_month_names_de_DE, + &my_locale_typelib_ab_month_names_de_DE, + &my_locale_typelib_day_names_de_DE, + &my_locale_typelib_ab_day_names_de_DE +); /***** LOCALE END de_DE *****/ /***** LOCALE BEGIN en_US: English - United States *****/ @@ -292,7 +400,17 @@ static TYPELIB my_locale_typelib_day_names_en_US = { array_elements(my_locale_day_names_en_US)-1, "", my_locale_day_names_en_US, NULL }; static TYPELIB my_locale_typelib_ab_day_names_en_US = { array_elements(my_locale_ab_day_names_en_US)-1, "", my_locale_ab_day_names_en_US, NULL }; -MY_LOCALE my_locale_en_US ( "en_US", "English - United States", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US ); +MY_LOCALE my_locale_en_US +( + 0, + "en_US", + "English - United States", + TRUE, + &my_locale_typelib_month_names_en_US, + &my_locale_typelib_ab_month_names_en_US, + &my_locale_typelib_day_names_en_US, + &my_locale_typelib_ab_day_names_en_US +); /***** LOCALE END en_US *****/ /***** LOCALE BEGIN es_ES: Spanish - Spain *****/ @@ -312,7 +430,17 @@ static TYPELIB my_locale_typelib_day_names_es_ES = { array_elements(my_locale_day_names_es_ES)-1, "", my_locale_day_names_es_ES, NULL }; static TYPELIB my_locale_typelib_ab_day_names_es_ES = { array_elements(my_locale_ab_day_names_es_ES)-1, "", my_locale_ab_day_names_es_ES, NULL }; -MY_LOCALE my_locale_es_ES ( "es_ES", "Spanish - Spain", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_ES +( + 17, + "es_ES", + "Spanish - Spain", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_ES *****/ /***** LOCALE BEGIN et_EE: Estonian - Estonia *****/ @@ -332,7 +460,17 @@ static TYPELIB my_locale_typelib_day_names_et_EE = { array_elements(my_locale_day_names_et_EE)-1, "", my_locale_day_names_et_EE, NULL }; static TYPELIB my_locale_typelib_ab_day_names_et_EE = { array_elements(my_locale_ab_day_names_et_EE)-1, "", my_locale_ab_day_names_et_EE, NULL }; -MY_LOCALE my_locale_et_EE ( "et_EE", "Estonian - Estonia", FALSE, &my_locale_typelib_month_names_et_EE, &my_locale_typelib_ab_month_names_et_EE, &my_locale_typelib_day_names_et_EE, &my_locale_typelib_ab_day_names_et_EE ); +MY_LOCALE my_locale_et_EE +( + 18, + "et_EE", + "Estonian - Estonia", + FALSE, + &my_locale_typelib_month_names_et_EE, + &my_locale_typelib_ab_month_names_et_EE, + &my_locale_typelib_day_names_et_EE, + &my_locale_typelib_ab_day_names_et_EE +); /***** LOCALE END et_EE *****/ /***** LOCALE BEGIN eu_ES: Basque - Basque *****/ @@ -352,7 +490,17 @@ static TYPELIB my_locale_typelib_day_names_eu_ES = { array_elements(my_locale_day_names_eu_ES)-1, "", my_locale_day_names_eu_ES, NULL }; static TYPELIB my_locale_typelib_ab_day_names_eu_ES = { array_elements(my_locale_ab_day_names_eu_ES)-1, "", my_locale_ab_day_names_eu_ES, NULL }; -MY_LOCALE my_locale_eu_ES ( "eu_ES", "Basque - Basque", TRUE, &my_locale_typelib_month_names_eu_ES, &my_locale_typelib_ab_month_names_eu_ES, &my_locale_typelib_day_names_eu_ES, &my_locale_typelib_ab_day_names_eu_ES ); +MY_LOCALE my_locale_eu_ES +( + 19, + "eu_ES", + "Basque - Basque", + TRUE, + &my_locale_typelib_month_names_eu_ES, + &my_locale_typelib_ab_month_names_eu_ES, + &my_locale_typelib_day_names_eu_ES, + &my_locale_typelib_ab_day_names_eu_ES +); /***** LOCALE END eu_ES *****/ /***** LOCALE BEGIN fi_FI: Finnish - Finland *****/ @@ -372,7 +520,17 @@ static TYPELIB my_locale_typelib_day_names_fi_FI = { array_elements(my_locale_day_names_fi_FI)-1, "", my_locale_day_names_fi_FI, NULL }; static TYPELIB my_locale_typelib_ab_day_names_fi_FI = { array_elements(my_locale_ab_day_names_fi_FI)-1, "", my_locale_ab_day_names_fi_FI, NULL }; -MY_LOCALE my_locale_fi_FI ( "fi_FI", "Finnish - Finland", FALSE, &my_locale_typelib_month_names_fi_FI, &my_locale_typelib_ab_month_names_fi_FI, &my_locale_typelib_day_names_fi_FI, &my_locale_typelib_ab_day_names_fi_FI ); +MY_LOCALE my_locale_fi_FI +( + 20, + "fi_FI", + "Finnish - Finland", + FALSE, + &my_locale_typelib_month_names_fi_FI, + &my_locale_typelib_ab_month_names_fi_FI, + &my_locale_typelib_day_names_fi_FI, + &my_locale_typelib_ab_day_names_fi_FI +); /***** LOCALE END fi_FI *****/ /***** LOCALE BEGIN fo_FO: Faroese - Faroe Islands *****/ @@ -392,7 +550,17 @@ static TYPELIB my_locale_typelib_day_names_fo_FO = { array_elements(my_locale_day_names_fo_FO)-1, "", my_locale_day_names_fo_FO, NULL }; static TYPELIB my_locale_typelib_ab_day_names_fo_FO = { array_elements(my_locale_ab_day_names_fo_FO)-1, "", my_locale_ab_day_names_fo_FO, NULL }; -MY_LOCALE my_locale_fo_FO ( "fo_FO", "Faroese - Faroe Islands", FALSE, &my_locale_typelib_month_names_fo_FO, &my_locale_typelib_ab_month_names_fo_FO, &my_locale_typelib_day_names_fo_FO, &my_locale_typelib_ab_day_names_fo_FO ); +MY_LOCALE my_locale_fo_FO +( + 21, + "fo_FO", + "Faroese - Faroe Islands", + FALSE, + &my_locale_typelib_month_names_fo_FO, + &my_locale_typelib_ab_month_names_fo_FO, + &my_locale_typelib_day_names_fo_FO, + &my_locale_typelib_ab_day_names_fo_FO +); /***** LOCALE END fo_FO *****/ /***** LOCALE BEGIN fr_FR: French - France *****/ @@ -412,7 +580,17 @@ static TYPELIB my_locale_typelib_day_names_fr_FR = { array_elements(my_locale_day_names_fr_FR)-1, "", my_locale_day_names_fr_FR, NULL }; static TYPELIB my_locale_typelib_ab_day_names_fr_FR = { array_elements(my_locale_ab_day_names_fr_FR)-1, "", my_locale_ab_day_names_fr_FR, NULL }; -MY_LOCALE my_locale_fr_FR ( "fr_FR", "French - France", FALSE, &my_locale_typelib_month_names_fr_FR, &my_locale_typelib_ab_month_names_fr_FR, &my_locale_typelib_day_names_fr_FR, &my_locale_typelib_ab_day_names_fr_FR ); +MY_LOCALE my_locale_fr_FR +( + 5, + "fr_FR", + "French - France", + FALSE, + &my_locale_typelib_month_names_fr_FR, + &my_locale_typelib_ab_month_names_fr_FR, + &my_locale_typelib_day_names_fr_FR, + &my_locale_typelib_ab_day_names_fr_FR +); /***** LOCALE END fr_FR *****/ /***** LOCALE BEGIN gl_ES: Galician - Galician *****/ @@ -432,7 +610,17 @@ static TYPELIB my_locale_typelib_day_names_gl_ES = { array_elements(my_locale_day_names_gl_ES)-1, "", my_locale_day_names_gl_ES, NULL }; static TYPELIB my_locale_typelib_ab_day_names_gl_ES = { array_elements(my_locale_ab_day_names_gl_ES)-1, "", my_locale_ab_day_names_gl_ES, NULL }; -MY_LOCALE my_locale_gl_ES ( "gl_ES", "Galician - Galician", FALSE, &my_locale_typelib_month_names_gl_ES, &my_locale_typelib_ab_month_names_gl_ES, &my_locale_typelib_day_names_gl_ES, &my_locale_typelib_ab_day_names_gl_ES ); +MY_LOCALE my_locale_gl_ES +( + 22, + "gl_ES", + "Galician - Galician", + FALSE, + &my_locale_typelib_month_names_gl_ES, + &my_locale_typelib_ab_month_names_gl_ES, + &my_locale_typelib_day_names_gl_ES, + &my_locale_typelib_ab_day_names_gl_ES +); /***** LOCALE END gl_ES *****/ /***** LOCALE BEGIN gu_IN: Gujarati - India *****/ @@ -452,7 +640,17 @@ static TYPELIB my_locale_typelib_day_names_gu_IN = { array_elements(my_locale_day_names_gu_IN)-1, "", my_locale_day_names_gu_IN, NULL }; static TYPELIB my_locale_typelib_ab_day_names_gu_IN = { array_elements(my_locale_ab_day_names_gu_IN)-1, "", my_locale_ab_day_names_gu_IN, NULL }; -MY_LOCALE my_locale_gu_IN ( "gu_IN", "Gujarati - India", FALSE, &my_locale_typelib_month_names_gu_IN, &my_locale_typelib_ab_month_names_gu_IN, &my_locale_typelib_day_names_gu_IN, &my_locale_typelib_ab_day_names_gu_IN ); +MY_LOCALE my_locale_gu_IN +( + 23, + "gu_IN", + "Gujarati - India", + FALSE, + &my_locale_typelib_month_names_gu_IN, + &my_locale_typelib_ab_month_names_gu_IN, + &my_locale_typelib_day_names_gu_IN, + &my_locale_typelib_ab_day_names_gu_IN +); /***** LOCALE END gu_IN *****/ /***** LOCALE BEGIN he_IL: Hebrew - Israel *****/ @@ -472,7 +670,17 @@ static TYPELIB my_locale_typelib_day_names_he_IL = { array_elements(my_locale_day_names_he_IL)-1, "", my_locale_day_names_he_IL, NULL }; static TYPELIB my_locale_typelib_ab_day_names_he_IL = { array_elements(my_locale_ab_day_names_he_IL)-1, "", my_locale_ab_day_names_he_IL, NULL }; -MY_LOCALE my_locale_he_IL ( "he_IL", "Hebrew - Israel", FALSE, &my_locale_typelib_month_names_he_IL, &my_locale_typelib_ab_month_names_he_IL, &my_locale_typelib_day_names_he_IL, &my_locale_typelib_ab_day_names_he_IL ); +MY_LOCALE my_locale_he_IL +( + 24, + "he_IL", + "Hebrew - Israel", + FALSE, + &my_locale_typelib_month_names_he_IL, + &my_locale_typelib_ab_month_names_he_IL, + &my_locale_typelib_day_names_he_IL, + &my_locale_typelib_ab_day_names_he_IL +); /***** LOCALE END he_IL *****/ /***** LOCALE BEGIN hi_IN: Hindi - India *****/ @@ -492,7 +700,17 @@ static TYPELIB my_locale_typelib_day_names_hi_IN = { array_elements(my_locale_day_names_hi_IN)-1, "", my_locale_day_names_hi_IN, NULL }; static TYPELIB my_locale_typelib_ab_day_names_hi_IN = { array_elements(my_locale_ab_day_names_hi_IN)-1, "", my_locale_ab_day_names_hi_IN, NULL }; -MY_LOCALE my_locale_hi_IN ( "hi_IN", "Hindi - India", FALSE, &my_locale_typelib_month_names_hi_IN, &my_locale_typelib_ab_month_names_hi_IN, &my_locale_typelib_day_names_hi_IN, &my_locale_typelib_ab_day_names_hi_IN ); +MY_LOCALE my_locale_hi_IN +( + 25, + "hi_IN", + "Hindi - India", + FALSE, + &my_locale_typelib_month_names_hi_IN, + &my_locale_typelib_ab_month_names_hi_IN, + &my_locale_typelib_day_names_hi_IN, + &my_locale_typelib_ab_day_names_hi_IN +); /***** LOCALE END hi_IN *****/ /***** LOCALE BEGIN hr_HR: Croatian - Croatia *****/ @@ -512,7 +730,17 @@ static TYPELIB my_locale_typelib_day_names_hr_HR = { array_elements(my_locale_day_names_hr_HR)-1, "", my_locale_day_names_hr_HR, NULL }; static TYPELIB my_locale_typelib_ab_day_names_hr_HR = { array_elements(my_locale_ab_day_names_hr_HR)-1, "", my_locale_ab_day_names_hr_HR, NULL }; -MY_LOCALE my_locale_hr_HR ( "hr_HR", "Croatian - Croatia", FALSE, &my_locale_typelib_month_names_hr_HR, &my_locale_typelib_ab_month_names_hr_HR, &my_locale_typelib_day_names_hr_HR, &my_locale_typelib_ab_day_names_hr_HR ); +MY_LOCALE my_locale_hr_HR +( + 26, + "hr_HR", + "Croatian - Croatia", + FALSE, + &my_locale_typelib_month_names_hr_HR, + &my_locale_typelib_ab_month_names_hr_HR, + &my_locale_typelib_day_names_hr_HR, + &my_locale_typelib_ab_day_names_hr_HR +); /***** LOCALE END hr_HR *****/ /***** LOCALE BEGIN hu_HU: Hungarian - Hungary *****/ @@ -532,7 +760,17 @@ static TYPELIB my_locale_typelib_day_names_hu_HU = { array_elements(my_locale_day_names_hu_HU)-1, "", my_locale_day_names_hu_HU, NULL }; static TYPELIB my_locale_typelib_ab_day_names_hu_HU = { array_elements(my_locale_ab_day_names_hu_HU)-1, "", my_locale_ab_day_names_hu_HU, NULL }; -MY_LOCALE my_locale_hu_HU ( "hu_HU", "Hungarian - Hungary", FALSE, &my_locale_typelib_month_names_hu_HU, &my_locale_typelib_ab_month_names_hu_HU, &my_locale_typelib_day_names_hu_HU, &my_locale_typelib_ab_day_names_hu_HU ); +MY_LOCALE my_locale_hu_HU +( + 27, + "hu_HU", + "Hungarian - Hungary", + FALSE, + &my_locale_typelib_month_names_hu_HU, + &my_locale_typelib_ab_month_names_hu_HU, + &my_locale_typelib_day_names_hu_HU, + &my_locale_typelib_ab_day_names_hu_HU +); /***** LOCALE END hu_HU *****/ /***** LOCALE BEGIN id_ID: Indonesian - Indonesia *****/ @@ -552,7 +790,17 @@ static TYPELIB my_locale_typelib_day_names_id_ID = { array_elements(my_locale_day_names_id_ID)-1, "", my_locale_day_names_id_ID, NULL }; static TYPELIB my_locale_typelib_ab_day_names_id_ID = { array_elements(my_locale_ab_day_names_id_ID)-1, "", my_locale_ab_day_names_id_ID, NULL }; -MY_LOCALE my_locale_id_ID ( "id_ID", "Indonesian - Indonesia", TRUE, &my_locale_typelib_month_names_id_ID, &my_locale_typelib_ab_month_names_id_ID, &my_locale_typelib_day_names_id_ID, &my_locale_typelib_ab_day_names_id_ID ); +MY_LOCALE my_locale_id_ID +( + 28, + "id_ID", + "Indonesian - Indonesia", + TRUE, + &my_locale_typelib_month_names_id_ID, + &my_locale_typelib_ab_month_names_id_ID, + &my_locale_typelib_day_names_id_ID, + &my_locale_typelib_ab_day_names_id_ID +); /***** LOCALE END id_ID *****/ /***** LOCALE BEGIN is_IS: Icelandic - Iceland *****/ @@ -572,7 +820,17 @@ static TYPELIB my_locale_typelib_day_names_is_IS = { array_elements(my_locale_day_names_is_IS)-1, "", my_locale_day_names_is_IS, NULL }; static TYPELIB my_locale_typelib_ab_day_names_is_IS = { array_elements(my_locale_ab_day_names_is_IS)-1, "", my_locale_ab_day_names_is_IS, NULL }; -MY_LOCALE my_locale_is_IS ( "is_IS", "Icelandic - Iceland", FALSE, &my_locale_typelib_month_names_is_IS, &my_locale_typelib_ab_month_names_is_IS, &my_locale_typelib_day_names_is_IS, &my_locale_typelib_ab_day_names_is_IS ); +MY_LOCALE my_locale_is_IS +( + 29, + "is_IS", + "Icelandic - Iceland", + FALSE, + &my_locale_typelib_month_names_is_IS, + &my_locale_typelib_ab_month_names_is_IS, + &my_locale_typelib_day_names_is_IS, + &my_locale_typelib_ab_day_names_is_IS +); /***** LOCALE END is_IS *****/ /***** LOCALE BEGIN it_CH: Italian - Switzerland *****/ @@ -592,7 +850,17 @@ static TYPELIB my_locale_typelib_day_names_it_CH = { array_elements(my_locale_day_names_it_CH)-1, "", my_locale_day_names_it_CH, NULL }; static TYPELIB my_locale_typelib_ab_day_names_it_CH = { array_elements(my_locale_ab_day_names_it_CH)-1, "", my_locale_ab_day_names_it_CH, NULL }; -MY_LOCALE my_locale_it_CH ( "it_CH", "Italian - Switzerland", FALSE, &my_locale_typelib_month_names_it_CH, &my_locale_typelib_ab_month_names_it_CH, &my_locale_typelib_day_names_it_CH, &my_locale_typelib_ab_day_names_it_CH ); +MY_LOCALE my_locale_it_CH +( + 30, + "it_CH", + "Italian - Switzerland", + FALSE, + &my_locale_typelib_month_names_it_CH, + &my_locale_typelib_ab_month_names_it_CH, + &my_locale_typelib_day_names_it_CH, + &my_locale_typelib_ab_day_names_it_CH +); /***** LOCALE END it_CH *****/ /***** LOCALE BEGIN ja_JP: Japanese - Japan *****/ @@ -612,7 +880,17 @@ static TYPELIB my_locale_typelib_day_names_ja_JP = { array_elements(my_locale_day_names_ja_JP)-1, "", my_locale_day_names_ja_JP, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ja_JP = { array_elements(my_locale_ab_day_names_ja_JP)-1, "", my_locale_ab_day_names_ja_JP, NULL }; -MY_LOCALE my_locale_ja_JP ( "ja_JP", "Japanese - Japan", FALSE, &my_locale_typelib_month_names_ja_JP, &my_locale_typelib_ab_month_names_ja_JP, &my_locale_typelib_day_names_ja_JP, &my_locale_typelib_ab_day_names_ja_JP ); +MY_LOCALE my_locale_ja_JP +( + 2, + "ja_JP", + "Japanese - Japan", + FALSE, + &my_locale_typelib_month_names_ja_JP, + &my_locale_typelib_ab_month_names_ja_JP, + &my_locale_typelib_day_names_ja_JP, + &my_locale_typelib_ab_day_names_ja_JP +); /***** LOCALE END ja_JP *****/ /***** LOCALE BEGIN ko_KR: Korean - Korea *****/ @@ -632,7 +910,17 @@ static TYPELIB my_locale_typelib_day_names_ko_KR = { array_elements(my_locale_day_names_ko_KR)-1, "", my_locale_day_names_ko_KR, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ko_KR = { array_elements(my_locale_ab_day_names_ko_KR)-1, "", my_locale_ab_day_names_ko_KR, NULL }; -MY_LOCALE my_locale_ko_KR ( "ko_KR", "Korean - Korea", FALSE, &my_locale_typelib_month_names_ko_KR, &my_locale_typelib_ab_month_names_ko_KR, &my_locale_typelib_day_names_ko_KR, &my_locale_typelib_ab_day_names_ko_KR ); +MY_LOCALE my_locale_ko_KR +( + 31, + "ko_KR", + "Korean - Korea", + FALSE, + &my_locale_typelib_month_names_ko_KR, + &my_locale_typelib_ab_month_names_ko_KR, + &my_locale_typelib_day_names_ko_KR, + &my_locale_typelib_ab_day_names_ko_KR +); /***** LOCALE END ko_KR *****/ /***** LOCALE BEGIN lt_LT: Lithuanian - Lithuania *****/ @@ -652,7 +940,17 @@ static TYPELIB my_locale_typelib_day_names_lt_LT = { array_elements(my_locale_day_names_lt_LT)-1, "", my_locale_day_names_lt_LT, NULL }; static TYPELIB my_locale_typelib_ab_day_names_lt_LT = { array_elements(my_locale_ab_day_names_lt_LT)-1, "", my_locale_ab_day_names_lt_LT, NULL }; -MY_LOCALE my_locale_lt_LT ( "lt_LT", "Lithuanian - Lithuania", FALSE, &my_locale_typelib_month_names_lt_LT, &my_locale_typelib_ab_month_names_lt_LT, &my_locale_typelib_day_names_lt_LT, &my_locale_typelib_ab_day_names_lt_LT ); +MY_LOCALE my_locale_lt_LT +( + 32, + "lt_LT", + "Lithuanian - Lithuania", + FALSE, + &my_locale_typelib_month_names_lt_LT, + &my_locale_typelib_ab_month_names_lt_LT, + &my_locale_typelib_day_names_lt_LT, + &my_locale_typelib_ab_day_names_lt_LT +); /***** LOCALE END lt_LT *****/ /***** LOCALE BEGIN lv_LV: Latvian - Latvia *****/ @@ -672,7 +970,17 @@ static TYPELIB my_locale_typelib_day_names_lv_LV = { array_elements(my_locale_day_names_lv_LV)-1, "", my_locale_day_names_lv_LV, NULL }; static TYPELIB my_locale_typelib_ab_day_names_lv_LV = { array_elements(my_locale_ab_day_names_lv_LV)-1, "", my_locale_ab_day_names_lv_LV, NULL }; -MY_LOCALE my_locale_lv_LV ( "lv_LV", "Latvian - Latvia", FALSE, &my_locale_typelib_month_names_lv_LV, &my_locale_typelib_ab_month_names_lv_LV, &my_locale_typelib_day_names_lv_LV, &my_locale_typelib_ab_day_names_lv_LV ); +MY_LOCALE my_locale_lv_LV +( + 33, + "lv_LV", + "Latvian - Latvia", + FALSE, + &my_locale_typelib_month_names_lv_LV, + &my_locale_typelib_ab_month_names_lv_LV, + &my_locale_typelib_day_names_lv_LV, + &my_locale_typelib_ab_day_names_lv_LV +); /***** LOCALE END lv_LV *****/ /***** LOCALE BEGIN mk_MK: Macedonian - FYROM *****/ @@ -692,7 +1000,17 @@ static TYPELIB my_locale_typelib_day_names_mk_MK = { array_elements(my_locale_day_names_mk_MK)-1, "", my_locale_day_names_mk_MK, NULL }; static TYPELIB my_locale_typelib_ab_day_names_mk_MK = { array_elements(my_locale_ab_day_names_mk_MK)-1, "", my_locale_ab_day_names_mk_MK, NULL }; -MY_LOCALE my_locale_mk_MK ( "mk_MK", "Macedonian - FYROM", FALSE, &my_locale_typelib_month_names_mk_MK, &my_locale_typelib_ab_month_names_mk_MK, &my_locale_typelib_day_names_mk_MK, &my_locale_typelib_ab_day_names_mk_MK ); +MY_LOCALE my_locale_mk_MK +( + 34, + "mk_MK", + "Macedonian - FYROM", + FALSE, + &my_locale_typelib_month_names_mk_MK, + &my_locale_typelib_ab_month_names_mk_MK, + &my_locale_typelib_day_names_mk_MK, + &my_locale_typelib_ab_day_names_mk_MK +); /***** LOCALE END mk_MK *****/ /***** LOCALE BEGIN mn_MN: Mongolia - Mongolian *****/ @@ -712,7 +1030,17 @@ static TYPELIB my_locale_typelib_day_names_mn_MN = { array_elements(my_locale_day_names_mn_MN)-1, "", my_locale_day_names_mn_MN, NULL }; static TYPELIB my_locale_typelib_ab_day_names_mn_MN = { array_elements(my_locale_ab_day_names_mn_MN)-1, "", my_locale_ab_day_names_mn_MN, NULL }; -MY_LOCALE my_locale_mn_MN ( "mn_MN", "Mongolia - Mongolian", FALSE, &my_locale_typelib_month_names_mn_MN, &my_locale_typelib_ab_month_names_mn_MN, &my_locale_typelib_day_names_mn_MN, &my_locale_typelib_ab_day_names_mn_MN ); +MY_LOCALE my_locale_mn_MN +( + 35, + "mn_MN", + "Mongolia - Mongolian", + FALSE, + &my_locale_typelib_month_names_mn_MN, + &my_locale_typelib_ab_month_names_mn_MN, + &my_locale_typelib_day_names_mn_MN, + &my_locale_typelib_ab_day_names_mn_MN +); /***** LOCALE END mn_MN *****/ /***** LOCALE BEGIN ms_MY: Malay - Malaysia *****/ @@ -732,7 +1060,17 @@ static TYPELIB my_locale_typelib_day_names_ms_MY = { array_elements(my_locale_day_names_ms_MY)-1, "", my_locale_day_names_ms_MY, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ms_MY = { array_elements(my_locale_ab_day_names_ms_MY)-1, "", my_locale_ab_day_names_ms_MY, NULL }; -MY_LOCALE my_locale_ms_MY ( "ms_MY", "Malay - Malaysia", TRUE, &my_locale_typelib_month_names_ms_MY, &my_locale_typelib_ab_month_names_ms_MY, &my_locale_typelib_day_names_ms_MY, &my_locale_typelib_ab_day_names_ms_MY ); +MY_LOCALE my_locale_ms_MY +( + 36, + "ms_MY", + "Malay - Malaysia", + TRUE, + &my_locale_typelib_month_names_ms_MY, + &my_locale_typelib_ab_month_names_ms_MY, + &my_locale_typelib_day_names_ms_MY, + &my_locale_typelib_ab_day_names_ms_MY +); /***** LOCALE END ms_MY *****/ /***** LOCALE BEGIN nb_NO: Norwegian(Bokml) - Norway *****/ @@ -752,7 +1090,17 @@ static TYPELIB my_locale_typelib_day_names_nb_NO = { array_elements(my_locale_day_names_nb_NO)-1, "", my_locale_day_names_nb_NO, NULL }; static TYPELIB my_locale_typelib_ab_day_names_nb_NO = { array_elements(my_locale_ab_day_names_nb_NO)-1, "", my_locale_ab_day_names_nb_NO, NULL }; -MY_LOCALE my_locale_nb_NO ( "nb_NO", "Norwegian(Bokml) - Norway", FALSE, &my_locale_typelib_month_names_nb_NO, &my_locale_typelib_ab_month_names_nb_NO, &my_locale_typelib_day_names_nb_NO, &my_locale_typelib_ab_day_names_nb_NO ); +MY_LOCALE my_locale_nb_NO +( + 37, + "nb_NO", + "Norwegian(Bokml) - Norway", + FALSE, + &my_locale_typelib_month_names_nb_NO, + &my_locale_typelib_ab_month_names_nb_NO, + &my_locale_typelib_day_names_nb_NO, + &my_locale_typelib_ab_day_names_nb_NO +); /***** LOCALE END nb_NO *****/ /***** LOCALE BEGIN nl_NL: Dutch - The Netherlands *****/ @@ -772,7 +1120,17 @@ static TYPELIB my_locale_typelib_day_names_nl_NL = { array_elements(my_locale_day_names_nl_NL)-1, "", my_locale_day_names_nl_NL, NULL }; static TYPELIB my_locale_typelib_ab_day_names_nl_NL = { array_elements(my_locale_ab_day_names_nl_NL)-1, "", my_locale_ab_day_names_nl_NL, NULL }; -MY_LOCALE my_locale_nl_NL ( "nl_NL", "Dutch - The Netherlands", TRUE, &my_locale_typelib_month_names_nl_NL, &my_locale_typelib_ab_month_names_nl_NL, &my_locale_typelib_day_names_nl_NL, &my_locale_typelib_ab_day_names_nl_NL ); +MY_LOCALE my_locale_nl_NL +( + 38, + "nl_NL", + "Dutch - The Netherlands", + TRUE, + &my_locale_typelib_month_names_nl_NL, + &my_locale_typelib_ab_month_names_nl_NL, + &my_locale_typelib_day_names_nl_NL, + &my_locale_typelib_ab_day_names_nl_NL +); /***** LOCALE END nl_NL *****/ /***** LOCALE BEGIN pl_PL: Polish - Poland *****/ @@ -792,7 +1150,17 @@ static TYPELIB my_locale_typelib_day_names_pl_PL = { array_elements(my_locale_day_names_pl_PL)-1, "", my_locale_day_names_pl_PL, NULL }; static TYPELIB my_locale_typelib_ab_day_names_pl_PL = { array_elements(my_locale_ab_day_names_pl_PL)-1, "", my_locale_ab_day_names_pl_PL, NULL }; -MY_LOCALE my_locale_pl_PL ( "pl_PL", "Polish - Poland", FALSE, &my_locale_typelib_month_names_pl_PL, &my_locale_typelib_ab_month_names_pl_PL, &my_locale_typelib_day_names_pl_PL, &my_locale_typelib_ab_day_names_pl_PL ); +MY_LOCALE my_locale_pl_PL +( + 39, + "pl_PL", + "Polish - Poland", + FALSE, + &my_locale_typelib_month_names_pl_PL, + &my_locale_typelib_ab_month_names_pl_PL, + &my_locale_typelib_day_names_pl_PL, + &my_locale_typelib_ab_day_names_pl_PL +); /***** LOCALE END pl_PL *****/ /***** LOCALE BEGIN pt_BR: Portugese - Brazil *****/ @@ -812,7 +1180,17 @@ static TYPELIB my_locale_typelib_day_names_pt_BR = { array_elements(my_locale_day_names_pt_BR)-1, "", my_locale_day_names_pt_BR, NULL }; static TYPELIB my_locale_typelib_ab_day_names_pt_BR = { array_elements(my_locale_ab_day_names_pt_BR)-1, "", my_locale_ab_day_names_pt_BR, NULL }; -MY_LOCALE my_locale_pt_BR ( "pt_BR", "Portugese - Brazil", FALSE, &my_locale_typelib_month_names_pt_BR, &my_locale_typelib_ab_month_names_pt_BR, &my_locale_typelib_day_names_pt_BR, &my_locale_typelib_ab_day_names_pt_BR ); +MY_LOCALE my_locale_pt_BR +( + 40, + "pt_BR", + "Portugese - Brazil", + FALSE, + &my_locale_typelib_month_names_pt_BR, + &my_locale_typelib_ab_month_names_pt_BR, + &my_locale_typelib_day_names_pt_BR, + &my_locale_typelib_ab_day_names_pt_BR +); /***** LOCALE END pt_BR *****/ /***** LOCALE BEGIN pt_PT: Portugese - Portugal *****/ @@ -832,7 +1210,17 @@ static TYPELIB my_locale_typelib_day_names_pt_PT = { array_elements(my_locale_day_names_pt_PT)-1, "", my_locale_day_names_pt_PT, NULL }; static TYPELIB my_locale_typelib_ab_day_names_pt_PT = { array_elements(my_locale_ab_day_names_pt_PT)-1, "", my_locale_ab_day_names_pt_PT, NULL }; -MY_LOCALE my_locale_pt_PT ( "pt_PT", "Portugese - Portugal", FALSE, &my_locale_typelib_month_names_pt_PT, &my_locale_typelib_ab_month_names_pt_PT, &my_locale_typelib_day_names_pt_PT, &my_locale_typelib_ab_day_names_pt_PT ); +MY_LOCALE my_locale_pt_PT +( + 41, + "pt_PT", + "Portugese - Portugal", + FALSE, + &my_locale_typelib_month_names_pt_PT, + &my_locale_typelib_ab_month_names_pt_PT, + &my_locale_typelib_day_names_pt_PT, + &my_locale_typelib_ab_day_names_pt_PT +); /***** LOCALE END pt_PT *****/ /***** LOCALE BEGIN ro_RO: Romanian - Romania *****/ @@ -852,7 +1240,17 @@ static TYPELIB my_locale_typelib_day_names_ro_RO = { array_elements(my_locale_day_names_ro_RO)-1, "", my_locale_day_names_ro_RO, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ro_RO = { array_elements(my_locale_ab_day_names_ro_RO)-1, "", my_locale_ab_day_names_ro_RO, NULL }; -MY_LOCALE my_locale_ro_RO ( "ro_RO", "Romanian - Romania", FALSE, &my_locale_typelib_month_names_ro_RO, &my_locale_typelib_ab_month_names_ro_RO, &my_locale_typelib_day_names_ro_RO, &my_locale_typelib_ab_day_names_ro_RO ); +MY_LOCALE my_locale_ro_RO +( + 42, + "ro_RO", + "Romanian - Romania", + FALSE, + &my_locale_typelib_month_names_ro_RO, + &my_locale_typelib_ab_month_names_ro_RO, + &my_locale_typelib_day_names_ro_RO, + &my_locale_typelib_ab_day_names_ro_RO +); /***** LOCALE END ro_RO *****/ /***** LOCALE BEGIN ru_RU: Russian - Russia *****/ @@ -872,7 +1270,17 @@ static TYPELIB my_locale_typelib_day_names_ru_RU = { array_elements(my_locale_day_names_ru_RU)-1, "", my_locale_day_names_ru_RU, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ru_RU = { array_elements(my_locale_ab_day_names_ru_RU)-1, "", my_locale_ab_day_names_ru_RU, NULL }; -MY_LOCALE my_locale_ru_RU ( "ru_RU", "Russian - Russia", FALSE, &my_locale_typelib_month_names_ru_RU, &my_locale_typelib_ab_month_names_ru_RU, &my_locale_typelib_day_names_ru_RU, &my_locale_typelib_ab_day_names_ru_RU ); +MY_LOCALE my_locale_ru_RU +( + 43, + "ru_RU", + "Russian - Russia", + FALSE, + &my_locale_typelib_month_names_ru_RU, + &my_locale_typelib_ab_month_names_ru_RU, + &my_locale_typelib_day_names_ru_RU, + &my_locale_typelib_ab_day_names_ru_RU +); /***** LOCALE END ru_RU *****/ /***** LOCALE BEGIN ru_UA: Russian - Ukraine *****/ @@ -892,7 +1300,17 @@ static TYPELIB my_locale_typelib_day_names_ru_UA = { array_elements(my_locale_day_names_ru_UA)-1, "", my_locale_day_names_ru_UA, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ru_UA = { array_elements(my_locale_ab_day_names_ru_UA)-1, "", my_locale_ab_day_names_ru_UA, NULL }; -MY_LOCALE my_locale_ru_UA ( "ru_UA", "Russian - Ukraine", FALSE, &my_locale_typelib_month_names_ru_UA, &my_locale_typelib_ab_month_names_ru_UA, &my_locale_typelib_day_names_ru_UA, &my_locale_typelib_ab_day_names_ru_UA ); +MY_LOCALE my_locale_ru_UA +( + 44, + "ru_UA", + "Russian - Ukraine", + FALSE, + &my_locale_typelib_month_names_ru_UA, + &my_locale_typelib_ab_month_names_ru_UA, + &my_locale_typelib_day_names_ru_UA, + &my_locale_typelib_ab_day_names_ru_UA +); /***** LOCALE END ru_UA *****/ /***** LOCALE BEGIN sk_SK: Slovak - Slovakia *****/ @@ -912,7 +1330,17 @@ static TYPELIB my_locale_typelib_day_names_sk_SK = { array_elements(my_locale_day_names_sk_SK)-1, "", my_locale_day_names_sk_SK, NULL }; static TYPELIB my_locale_typelib_ab_day_names_sk_SK = { array_elements(my_locale_ab_day_names_sk_SK)-1, "", my_locale_ab_day_names_sk_SK, NULL }; -MY_LOCALE my_locale_sk_SK ( "sk_SK", "Slovak - Slovakia", FALSE, &my_locale_typelib_month_names_sk_SK, &my_locale_typelib_ab_month_names_sk_SK, &my_locale_typelib_day_names_sk_SK, &my_locale_typelib_ab_day_names_sk_SK ); +MY_LOCALE my_locale_sk_SK +( + 45, + "sk_SK", + "Slovak - Slovakia", + FALSE, + &my_locale_typelib_month_names_sk_SK, + &my_locale_typelib_ab_month_names_sk_SK, + &my_locale_typelib_day_names_sk_SK, + &my_locale_typelib_ab_day_names_sk_SK +); /***** LOCALE END sk_SK *****/ /***** LOCALE BEGIN sl_SI: Slovenian - Slovenia *****/ @@ -932,7 +1360,17 @@ static TYPELIB my_locale_typelib_day_names_sl_SI = { array_elements(my_locale_day_names_sl_SI)-1, "", my_locale_day_names_sl_SI, NULL }; static TYPELIB my_locale_typelib_ab_day_names_sl_SI = { array_elements(my_locale_ab_day_names_sl_SI)-1, "", my_locale_ab_day_names_sl_SI, NULL }; -MY_LOCALE my_locale_sl_SI ( "sl_SI", "Slovenian - Slovenia", FALSE, &my_locale_typelib_month_names_sl_SI, &my_locale_typelib_ab_month_names_sl_SI, &my_locale_typelib_day_names_sl_SI, &my_locale_typelib_ab_day_names_sl_SI ); +MY_LOCALE my_locale_sl_SI +( + 46, + "sl_SI", + "Slovenian - Slovenia", + FALSE, + &my_locale_typelib_month_names_sl_SI, + &my_locale_typelib_ab_month_names_sl_SI, + &my_locale_typelib_day_names_sl_SI, + &my_locale_typelib_ab_day_names_sl_SI +); /***** LOCALE END sl_SI *****/ /***** LOCALE BEGIN sq_AL: Albanian - Albania *****/ @@ -952,7 +1390,17 @@ static TYPELIB my_locale_typelib_day_names_sq_AL = { array_elements(my_locale_day_names_sq_AL)-1, "", my_locale_day_names_sq_AL, NULL }; static TYPELIB my_locale_typelib_ab_day_names_sq_AL = { array_elements(my_locale_ab_day_names_sq_AL)-1, "", my_locale_ab_day_names_sq_AL, NULL }; -MY_LOCALE my_locale_sq_AL ( "sq_AL", "Albanian - Albania", FALSE, &my_locale_typelib_month_names_sq_AL, &my_locale_typelib_ab_month_names_sq_AL, &my_locale_typelib_day_names_sq_AL, &my_locale_typelib_ab_day_names_sq_AL ); +MY_LOCALE my_locale_sq_AL +( + 47, + "sq_AL", + "Albanian - Albania", + FALSE, + &my_locale_typelib_month_names_sq_AL, + &my_locale_typelib_ab_month_names_sq_AL, + &my_locale_typelib_day_names_sq_AL, + &my_locale_typelib_ab_day_names_sq_AL +); /***** LOCALE END sq_AL *****/ /***** LOCALE BEGIN sr_YU: Servian - Yugoslavia *****/ @@ -972,7 +1420,17 @@ static TYPELIB my_locale_typelib_day_names_sr_YU = { array_elements(my_locale_day_names_sr_YU)-1, "", my_locale_day_names_sr_YU, NULL }; static TYPELIB my_locale_typelib_ab_day_names_sr_YU = { array_elements(my_locale_ab_day_names_sr_YU)-1, "", my_locale_ab_day_names_sr_YU, NULL }; -MY_LOCALE my_locale_sr_YU ( "sr_YU", "Servian - Yugoslavia", FALSE, &my_locale_typelib_month_names_sr_YU, &my_locale_typelib_ab_month_names_sr_YU, &my_locale_typelib_day_names_sr_YU, &my_locale_typelib_ab_day_names_sr_YU ); +MY_LOCALE my_locale_sr_YU +( + 48, + "sr_YU", + "Servian - Yugoslavia", + FALSE, + &my_locale_typelib_month_names_sr_YU, + &my_locale_typelib_ab_month_names_sr_YU, + &my_locale_typelib_day_names_sr_YU, + &my_locale_typelib_ab_day_names_sr_YU +); /***** LOCALE END sr_YU *****/ /***** LOCALE BEGIN sv_SE: Swedish - Sweden *****/ @@ -992,7 +1450,17 @@ static TYPELIB my_locale_typelib_day_names_sv_SE = { array_elements(my_locale_day_names_sv_SE)-1, "", my_locale_day_names_sv_SE, NULL }; static TYPELIB my_locale_typelib_ab_day_names_sv_SE = { array_elements(my_locale_ab_day_names_sv_SE)-1, "", my_locale_ab_day_names_sv_SE, NULL }; -MY_LOCALE my_locale_sv_SE ( "sv_SE", "Swedish - Sweden", FALSE, &my_locale_typelib_month_names_sv_SE, &my_locale_typelib_ab_month_names_sv_SE, &my_locale_typelib_day_names_sv_SE, &my_locale_typelib_ab_day_names_sv_SE ); +MY_LOCALE my_locale_sv_SE +( + 3, + "sv_SE", + "Swedish - Sweden", + FALSE, + &my_locale_typelib_month_names_sv_SE, + &my_locale_typelib_ab_month_names_sv_SE, + &my_locale_typelib_day_names_sv_SE, + &my_locale_typelib_ab_day_names_sv_SE +); /***** LOCALE END sv_SE *****/ /***** LOCALE BEGIN ta_IN: Tamil - India *****/ @@ -1012,7 +1480,17 @@ static TYPELIB my_locale_typelib_day_names_ta_IN = { array_elements(my_locale_day_names_ta_IN)-1, "", my_locale_day_names_ta_IN, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ta_IN = { array_elements(my_locale_ab_day_names_ta_IN)-1, "", my_locale_ab_day_names_ta_IN, NULL }; -MY_LOCALE my_locale_ta_IN ( "ta_IN", "Tamil - India", FALSE, &my_locale_typelib_month_names_ta_IN, &my_locale_typelib_ab_month_names_ta_IN, &my_locale_typelib_day_names_ta_IN, &my_locale_typelib_ab_day_names_ta_IN ); +MY_LOCALE my_locale_ta_IN +( + 49, + "ta_IN", + "Tamil - India", + FALSE, + &my_locale_typelib_month_names_ta_IN, + &my_locale_typelib_ab_month_names_ta_IN, + &my_locale_typelib_day_names_ta_IN, + &my_locale_typelib_ab_day_names_ta_IN +); /***** LOCALE END ta_IN *****/ /***** LOCALE BEGIN te_IN: Telugu - India *****/ @@ -1032,7 +1510,17 @@ static TYPELIB my_locale_typelib_day_names_te_IN = { array_elements(my_locale_day_names_te_IN)-1, "", my_locale_day_names_te_IN, NULL }; static TYPELIB my_locale_typelib_ab_day_names_te_IN = { array_elements(my_locale_ab_day_names_te_IN)-1, "", my_locale_ab_day_names_te_IN, NULL }; -MY_LOCALE my_locale_te_IN ( "te_IN", "Telugu - India", FALSE, &my_locale_typelib_month_names_te_IN, &my_locale_typelib_ab_month_names_te_IN, &my_locale_typelib_day_names_te_IN, &my_locale_typelib_ab_day_names_te_IN ); +MY_LOCALE my_locale_te_IN +( + 50, + "te_IN", + "Telugu - India", + FALSE, + &my_locale_typelib_month_names_te_IN, + &my_locale_typelib_ab_month_names_te_IN, + &my_locale_typelib_day_names_te_IN, + &my_locale_typelib_ab_day_names_te_IN +); /***** LOCALE END te_IN *****/ /***** LOCALE BEGIN th_TH: Thai - Thailand *****/ @@ -1052,7 +1540,17 @@ static TYPELIB my_locale_typelib_day_names_th_TH = { array_elements(my_locale_day_names_th_TH)-1, "", my_locale_day_names_th_TH, NULL }; static TYPELIB my_locale_typelib_ab_day_names_th_TH = { array_elements(my_locale_ab_day_names_th_TH)-1, "", my_locale_ab_day_names_th_TH, NULL }; -MY_LOCALE my_locale_th_TH ( "th_TH", "Thai - Thailand", FALSE, &my_locale_typelib_month_names_th_TH, &my_locale_typelib_ab_month_names_th_TH, &my_locale_typelib_day_names_th_TH, &my_locale_typelib_ab_day_names_th_TH ); +MY_LOCALE my_locale_th_TH +( + 51, + "th_TH", + "Thai - Thailand", + FALSE, + &my_locale_typelib_month_names_th_TH, + &my_locale_typelib_ab_month_names_th_TH, + &my_locale_typelib_day_names_th_TH, + &my_locale_typelib_ab_day_names_th_TH +); /***** LOCALE END th_TH *****/ /***** LOCALE BEGIN tr_TR: Turkish - Turkey *****/ @@ -1072,7 +1570,17 @@ static TYPELIB my_locale_typelib_day_names_tr_TR = { array_elements(my_locale_day_names_tr_TR)-1, "", my_locale_day_names_tr_TR, NULL }; static TYPELIB my_locale_typelib_ab_day_names_tr_TR = { array_elements(my_locale_ab_day_names_tr_TR)-1, "", my_locale_ab_day_names_tr_TR, NULL }; -MY_LOCALE my_locale_tr_TR ( "tr_TR", "Turkish - Turkey", FALSE, &my_locale_typelib_month_names_tr_TR, &my_locale_typelib_ab_month_names_tr_TR, &my_locale_typelib_day_names_tr_TR, &my_locale_typelib_ab_day_names_tr_TR ); +MY_LOCALE my_locale_tr_TR +( + 52, + "tr_TR", + "Turkish - Turkey", + FALSE, + &my_locale_typelib_month_names_tr_TR, + &my_locale_typelib_ab_month_names_tr_TR, + &my_locale_typelib_day_names_tr_TR, + &my_locale_typelib_ab_day_names_tr_TR +); /***** LOCALE END tr_TR *****/ /***** LOCALE BEGIN uk_UA: Ukrainian - Ukraine *****/ @@ -1092,7 +1600,17 @@ static TYPELIB my_locale_typelib_day_names_uk_UA = { array_elements(my_locale_day_names_uk_UA)-1, "", my_locale_day_names_uk_UA, NULL }; static TYPELIB my_locale_typelib_ab_day_names_uk_UA = { array_elements(my_locale_ab_day_names_uk_UA)-1, "", my_locale_ab_day_names_uk_UA, NULL }; -MY_LOCALE my_locale_uk_UA ( "uk_UA", "Ukrainian - Ukraine", FALSE, &my_locale_typelib_month_names_uk_UA, &my_locale_typelib_ab_month_names_uk_UA, &my_locale_typelib_day_names_uk_UA, &my_locale_typelib_ab_day_names_uk_UA ); +MY_LOCALE my_locale_uk_UA +( + 53, + "uk_UA", + "Ukrainian - Ukraine", + FALSE, + &my_locale_typelib_month_names_uk_UA, + &my_locale_typelib_ab_month_names_uk_UA, + &my_locale_typelib_day_names_uk_UA, + &my_locale_typelib_ab_day_names_uk_UA +); /***** LOCALE END uk_UA *****/ /***** LOCALE BEGIN ur_PK: Urdu - Pakistan *****/ @@ -1112,7 +1630,17 @@ static TYPELIB my_locale_typelib_day_names_ur_PK = { array_elements(my_locale_day_names_ur_PK)-1, "", my_locale_day_names_ur_PK, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ur_PK = { array_elements(my_locale_ab_day_names_ur_PK)-1, "", my_locale_ab_day_names_ur_PK, NULL }; -MY_LOCALE my_locale_ur_PK ( "ur_PK", "Urdu - Pakistan", FALSE, &my_locale_typelib_month_names_ur_PK, &my_locale_typelib_ab_month_names_ur_PK, &my_locale_typelib_day_names_ur_PK, &my_locale_typelib_ab_day_names_ur_PK ); +MY_LOCALE my_locale_ur_PK +( + 54, + "ur_PK", + "Urdu - Pakistan", + FALSE, + &my_locale_typelib_month_names_ur_PK, + &my_locale_typelib_ab_month_names_ur_PK, + &my_locale_typelib_day_names_ur_PK, + &my_locale_typelib_ab_day_names_ur_PK +); /***** LOCALE END ur_PK *****/ /***** LOCALE BEGIN vi_VN: Vietnamese - Vietnam *****/ @@ -1132,7 +1660,17 @@ static TYPELIB my_locale_typelib_day_names_vi_VN = { array_elements(my_locale_day_names_vi_VN)-1, "", my_locale_day_names_vi_VN, NULL }; static TYPELIB my_locale_typelib_ab_day_names_vi_VN = { array_elements(my_locale_ab_day_names_vi_VN)-1, "", my_locale_ab_day_names_vi_VN, NULL }; -MY_LOCALE my_locale_vi_VN ( "vi_VN", "Vietnamese - Vietnam", FALSE, &my_locale_typelib_month_names_vi_VN, &my_locale_typelib_ab_month_names_vi_VN, &my_locale_typelib_day_names_vi_VN, &my_locale_typelib_ab_day_names_vi_VN ); +MY_LOCALE my_locale_vi_VN +( + 55, + "vi_VN", + "Vietnamese - Vietnam", + FALSE, + &my_locale_typelib_month_names_vi_VN, + &my_locale_typelib_ab_month_names_vi_VN, + &my_locale_typelib_day_names_vi_VN, + &my_locale_typelib_ab_day_names_vi_VN +); /***** LOCALE END vi_VN *****/ /***** LOCALE BEGIN zh_CN: Chinese - Peoples Republic of China *****/ @@ -1152,7 +1690,17 @@ static TYPELIB my_locale_typelib_day_names_zh_CN = { array_elements(my_locale_day_names_zh_CN)-1, "", my_locale_day_names_zh_CN, NULL }; static TYPELIB my_locale_typelib_ab_day_names_zh_CN = { array_elements(my_locale_ab_day_names_zh_CN)-1, "", my_locale_ab_day_names_zh_CN, NULL }; -MY_LOCALE my_locale_zh_CN ( "zh_CN", "Chinese - Peoples Republic of China", FALSE, &my_locale_typelib_month_names_zh_CN, &my_locale_typelib_ab_month_names_zh_CN, &my_locale_typelib_day_names_zh_CN, &my_locale_typelib_ab_day_names_zh_CN ); +MY_LOCALE my_locale_zh_CN +( + 56, + "zh_CN", + "Chinese - Peoples Republic of China", + FALSE, + &my_locale_typelib_month_names_zh_CN, + &my_locale_typelib_ab_month_names_zh_CN, + &my_locale_typelib_day_names_zh_CN, + &my_locale_typelib_ab_day_names_zh_CN +); /***** LOCALE END zh_CN *****/ /***** LOCALE BEGIN zh_TW: Chinese - Taiwan *****/ @@ -1172,217 +1720,754 @@ static TYPELIB my_locale_typelib_day_names_zh_TW = { array_elements(my_locale_day_names_zh_TW)-1, "", my_locale_day_names_zh_TW, NULL }; static TYPELIB my_locale_typelib_ab_day_names_zh_TW = { array_elements(my_locale_ab_day_names_zh_TW)-1, "", my_locale_ab_day_names_zh_TW, NULL }; -MY_LOCALE my_locale_zh_TW ( "zh_TW", "Chinese - Taiwan", FALSE, &my_locale_typelib_month_names_zh_TW, &my_locale_typelib_ab_month_names_zh_TW, &my_locale_typelib_day_names_zh_TW, &my_locale_typelib_ab_day_names_zh_TW ); +MY_LOCALE my_locale_zh_TW +( + 57, + "zh_TW", + "Chinese - Taiwan", + FALSE, + &my_locale_typelib_month_names_zh_TW, + &my_locale_typelib_ab_month_names_zh_TW, + &my_locale_typelib_day_names_zh_TW, + &my_locale_typelib_ab_day_names_zh_TW +); /***** LOCALE END zh_TW *****/ /***** LOCALE BEGIN ar_DZ: Arabic - Algeria *****/ -MY_LOCALE my_locale_ar_DZ ( "ar_DZ", "Arabic - Algeria", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); +MY_LOCALE my_locale_ar_DZ +( + 58, + "ar_DZ", + "Arabic - Algeria", + FALSE, + &my_locale_typelib_month_names_ar_BH, + &my_locale_typelib_ab_month_names_ar_BH, + &my_locale_typelib_day_names_ar_BH, + &my_locale_typelib_ab_day_names_ar_BH +); /***** LOCALE END ar_DZ *****/ /***** LOCALE BEGIN ar_EG: Arabic - Egypt *****/ -MY_LOCALE my_locale_ar_EG ( "ar_EG", "Arabic - Egypt", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); +MY_LOCALE my_locale_ar_EG +( + 59, + "ar_EG", + "Arabic - Egypt", + FALSE, + &my_locale_typelib_month_names_ar_BH, + &my_locale_typelib_ab_month_names_ar_BH, + &my_locale_typelib_day_names_ar_BH, + &my_locale_typelib_ab_day_names_ar_BH +); /***** LOCALE END ar_EG *****/ /***** LOCALE BEGIN ar_IN: Arabic - Iran *****/ -MY_LOCALE my_locale_ar_IN ( "ar_IN", "Arabic - Iran", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); +MY_LOCALE my_locale_ar_IN +( + 60, + "ar_IN", + "Arabic - Iran", + FALSE, + &my_locale_typelib_month_names_ar_BH, + &my_locale_typelib_ab_month_names_ar_BH, + &my_locale_typelib_day_names_ar_BH, + &my_locale_typelib_ab_day_names_ar_BH +); /***** LOCALE END ar_IN *****/ /***** LOCALE BEGIN ar_IQ: Arabic - Iraq *****/ -MY_LOCALE my_locale_ar_IQ ( "ar_IQ", "Arabic - Iraq", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); +MY_LOCALE my_locale_ar_IQ +( + 61, + "ar_IQ", + "Arabic - Iraq", + FALSE, + &my_locale_typelib_month_names_ar_BH, + &my_locale_typelib_ab_month_names_ar_BH, + &my_locale_typelib_day_names_ar_BH, + &my_locale_typelib_ab_day_names_ar_BH +); /***** LOCALE END ar_IQ *****/ /***** LOCALE BEGIN ar_KW: Arabic - Kuwait *****/ -MY_LOCALE my_locale_ar_KW ( "ar_KW", "Arabic - Kuwait", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); +MY_LOCALE my_locale_ar_KW +( + 62, + "ar_KW", + "Arabic - Kuwait", + FALSE, + &my_locale_typelib_month_names_ar_BH, + &my_locale_typelib_ab_month_names_ar_BH, + &my_locale_typelib_day_names_ar_BH, + &my_locale_typelib_ab_day_names_ar_BH +); /***** LOCALE END ar_KW *****/ /***** LOCALE BEGIN ar_LB: Arabic - Lebanon *****/ -MY_LOCALE my_locale_ar_LB ( "ar_LB", "Arabic - Lebanon", FALSE, &my_locale_typelib_month_names_ar_JO, &my_locale_typelib_ab_month_names_ar_JO, &my_locale_typelib_day_names_ar_JO, &my_locale_typelib_ab_day_names_ar_JO ); +MY_LOCALE my_locale_ar_LB +( + 63, + "ar_LB", + "Arabic - Lebanon", + FALSE, + &my_locale_typelib_month_names_ar_JO, + &my_locale_typelib_ab_month_names_ar_JO, + &my_locale_typelib_day_names_ar_JO, + &my_locale_typelib_ab_day_names_ar_JO +); /***** LOCALE END ar_LB *****/ /***** LOCALE BEGIN ar_LY: Arabic - Libya *****/ -MY_LOCALE my_locale_ar_LY ( "ar_LY", "Arabic - Libya", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); +MY_LOCALE my_locale_ar_LY +( + 64, + "ar_LY", + "Arabic - Libya", + FALSE, + &my_locale_typelib_month_names_ar_BH, + &my_locale_typelib_ab_month_names_ar_BH, + &my_locale_typelib_day_names_ar_BH, + &my_locale_typelib_ab_day_names_ar_BH +); /***** LOCALE END ar_LY *****/ /***** LOCALE BEGIN ar_MA: Arabic - Morocco *****/ -MY_LOCALE my_locale_ar_MA ( "ar_MA", "Arabic - Morocco", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); +MY_LOCALE my_locale_ar_MA +( + 65, + "ar_MA", + "Arabic - Morocco", + FALSE, + &my_locale_typelib_month_names_ar_BH, + &my_locale_typelib_ab_month_names_ar_BH, + &my_locale_typelib_day_names_ar_BH, + &my_locale_typelib_ab_day_names_ar_BH +); /***** LOCALE END ar_MA *****/ /***** LOCALE BEGIN ar_OM: Arabic - Oman *****/ -MY_LOCALE my_locale_ar_OM ( "ar_OM", "Arabic - Oman", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); +MY_LOCALE my_locale_ar_OM +( + 66, + "ar_OM", + "Arabic - Oman", + FALSE, + &my_locale_typelib_month_names_ar_BH, + &my_locale_typelib_ab_month_names_ar_BH, + &my_locale_typelib_day_names_ar_BH, + &my_locale_typelib_ab_day_names_ar_BH +); /***** LOCALE END ar_OM *****/ /***** LOCALE BEGIN ar_QA: Arabic - Qatar *****/ -MY_LOCALE my_locale_ar_QA ( "ar_QA", "Arabic - Qatar", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); +MY_LOCALE my_locale_ar_QA +( + 67, + "ar_QA", + "Arabic - Qatar", + FALSE, + &my_locale_typelib_month_names_ar_BH, + &my_locale_typelib_ab_month_names_ar_BH, + &my_locale_typelib_day_names_ar_BH, + &my_locale_typelib_ab_day_names_ar_BH +); /***** LOCALE END ar_QA *****/ /***** LOCALE BEGIN ar_SD: Arabic - Sudan *****/ -MY_LOCALE my_locale_ar_SD ( "ar_SD", "Arabic - Sudan", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); +MY_LOCALE my_locale_ar_SD +( + 68, + "ar_SD", + "Arabic - Sudan", + FALSE, + &my_locale_typelib_month_names_ar_BH, + &my_locale_typelib_ab_month_names_ar_BH, + &my_locale_typelib_day_names_ar_BH, + &my_locale_typelib_ab_day_names_ar_BH +); /***** LOCALE END ar_SD *****/ /***** LOCALE BEGIN ar_TN: Arabic - Tunisia *****/ -MY_LOCALE my_locale_ar_TN ( "ar_TN", "Arabic - Tunisia", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); +MY_LOCALE my_locale_ar_TN +( + 69, + "ar_TN", + "Arabic - Tunisia", + FALSE, + &my_locale_typelib_month_names_ar_BH, + &my_locale_typelib_ab_month_names_ar_BH, + &my_locale_typelib_day_names_ar_BH, + &my_locale_typelib_ab_day_names_ar_BH +); /***** LOCALE END ar_TN *****/ /***** LOCALE BEGIN ar_YE: Arabic - Yemen *****/ -MY_LOCALE my_locale_ar_YE ( "ar_YE", "Arabic - Yemen", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); +MY_LOCALE my_locale_ar_YE +( + 70, + "ar_YE", + "Arabic - Yemen", + FALSE, + &my_locale_typelib_month_names_ar_BH, + &my_locale_typelib_ab_month_names_ar_BH, + &my_locale_typelib_day_names_ar_BH, + &my_locale_typelib_ab_day_names_ar_BH +); /***** LOCALE END ar_YE *****/ /***** LOCALE BEGIN de_BE: German - Belgium *****/ -MY_LOCALE my_locale_de_BE ( "de_BE", "German - Belgium", FALSE, &my_locale_typelib_month_names_de_DE, &my_locale_typelib_ab_month_names_de_DE, &my_locale_typelib_day_names_de_DE, &my_locale_typelib_ab_day_names_de_DE ); +MY_LOCALE my_locale_de_BE +( + 71, + "de_BE", + "German - Belgium", + FALSE, + &my_locale_typelib_month_names_de_DE, + &my_locale_typelib_ab_month_names_de_DE, + &my_locale_typelib_day_names_de_DE, + &my_locale_typelib_ab_day_names_de_DE +); /***** LOCALE END de_BE *****/ /***** LOCALE BEGIN de_CH: German - Switzerland *****/ -MY_LOCALE my_locale_de_CH ( "de_CH", "German - Switzerland", FALSE, &my_locale_typelib_month_names_de_DE, &my_locale_typelib_ab_month_names_de_DE, &my_locale_typelib_day_names_de_DE, &my_locale_typelib_ab_day_names_de_DE ); +MY_LOCALE my_locale_de_CH +( + 72, + "de_CH", + "German - Switzerland", + FALSE, + &my_locale_typelib_month_names_de_DE, + &my_locale_typelib_ab_month_names_de_DE, + &my_locale_typelib_day_names_de_DE, + &my_locale_typelib_ab_day_names_de_DE +); /***** LOCALE END de_CH *****/ /***** LOCALE BEGIN de_LU: German - Luxembourg *****/ -MY_LOCALE my_locale_de_LU ( "de_LU", "German - Luxembourg", FALSE, &my_locale_typelib_month_names_de_DE, &my_locale_typelib_ab_month_names_de_DE, &my_locale_typelib_day_names_de_DE, &my_locale_typelib_ab_day_names_de_DE ); +MY_LOCALE my_locale_de_LU +( + 73, + "de_LU", + "German - Luxembourg", + FALSE, + &my_locale_typelib_month_names_de_DE, + &my_locale_typelib_ab_month_names_de_DE, + &my_locale_typelib_day_names_de_DE, + &my_locale_typelib_ab_day_names_de_DE +); /***** LOCALE END de_LU *****/ /***** LOCALE BEGIN en_AU: English - Australia *****/ -MY_LOCALE my_locale_en_AU ( "en_AU", "English - Australia", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US ); +MY_LOCALE my_locale_en_AU +( + 74, + "en_AU", + "English - Australia", + TRUE, + &my_locale_typelib_month_names_en_US, + &my_locale_typelib_ab_month_names_en_US, + &my_locale_typelib_day_names_en_US, + &my_locale_typelib_ab_day_names_en_US +); /***** LOCALE END en_AU *****/ /***** LOCALE BEGIN en_CA: English - Canada *****/ -MY_LOCALE my_locale_en_CA ( "en_CA", "English - Canada", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US ); +MY_LOCALE my_locale_en_CA +( + 75, + "en_CA", + "English - Canada", + TRUE, + &my_locale_typelib_month_names_en_US, + &my_locale_typelib_ab_month_names_en_US, + &my_locale_typelib_day_names_en_US, + &my_locale_typelib_ab_day_names_en_US +); /***** LOCALE END en_CA *****/ /***** LOCALE BEGIN en_GB: English - United Kingdom *****/ -MY_LOCALE my_locale_en_GB ( "en_GB", "English - United Kingdom", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US ); +MY_LOCALE my_locale_en_GB +( + 1, + "en_GB", + "English - United Kingdom", + TRUE, + &my_locale_typelib_month_names_en_US, + &my_locale_typelib_ab_month_names_en_US, + &my_locale_typelib_day_names_en_US, + &my_locale_typelib_ab_day_names_en_US +); /***** LOCALE END en_GB *****/ /***** LOCALE BEGIN en_IN: English - India *****/ -MY_LOCALE my_locale_en_IN ( "en_IN", "English - India", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US ); +MY_LOCALE my_locale_en_IN +( + 76, + "en_IN", + "English - India", + TRUE, + &my_locale_typelib_month_names_en_US, + &my_locale_typelib_ab_month_names_en_US, + &my_locale_typelib_day_names_en_US, + &my_locale_typelib_ab_day_names_en_US +); /***** LOCALE END en_IN *****/ /***** LOCALE BEGIN en_NZ: English - New Zealand *****/ -MY_LOCALE my_locale_en_NZ ( "en_NZ", "English - New Zealand", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US ); +MY_LOCALE my_locale_en_NZ +( + 77, + "en_NZ", + "English - New Zealand", + TRUE, + &my_locale_typelib_month_names_en_US, + &my_locale_typelib_ab_month_names_en_US, + &my_locale_typelib_day_names_en_US, + &my_locale_typelib_ab_day_names_en_US +); /***** LOCALE END en_NZ *****/ /***** LOCALE BEGIN en_PH: English - Philippines *****/ -MY_LOCALE my_locale_en_PH ( "en_PH", "English - Philippines", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US ); +MY_LOCALE my_locale_en_PH +( + 78, + "en_PH", + "English - Philippines", + TRUE, + &my_locale_typelib_month_names_en_US, + &my_locale_typelib_ab_month_names_en_US, + &my_locale_typelib_day_names_en_US, + &my_locale_typelib_ab_day_names_en_US +); /***** LOCALE END en_PH *****/ /***** LOCALE BEGIN en_ZA: English - South Africa *****/ -MY_LOCALE my_locale_en_ZA ( "en_ZA", "English - South Africa", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US ); +MY_LOCALE my_locale_en_ZA +( + 79, + "en_ZA", + "English - South Africa", + TRUE, + &my_locale_typelib_month_names_en_US, + &my_locale_typelib_ab_month_names_en_US, + &my_locale_typelib_day_names_en_US, + &my_locale_typelib_ab_day_names_en_US +); /***** LOCALE END en_ZA *****/ /***** LOCALE BEGIN en_ZW: English - Zimbabwe *****/ -MY_LOCALE my_locale_en_ZW ( "en_ZW", "English - Zimbabwe", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US ); +MY_LOCALE my_locale_en_ZW +( + 80, + "en_ZW", + "English - Zimbabwe", + TRUE, + &my_locale_typelib_month_names_en_US, + &my_locale_typelib_ab_month_names_en_US, + &my_locale_typelib_day_names_en_US, + &my_locale_typelib_ab_day_names_en_US +); /***** LOCALE END en_ZW *****/ /***** LOCALE BEGIN es_AR: Spanish - Argentina *****/ -MY_LOCALE my_locale_es_AR ( "es_AR", "Spanish - Argentina", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_AR +( + 81, + "es_AR", + "Spanish - Argentina", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_AR *****/ /***** LOCALE BEGIN es_BO: Spanish - Bolivia *****/ -MY_LOCALE my_locale_es_BO ( "es_BO", "Spanish - Bolivia", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_BO +( + 82, + "es_BO", + "Spanish - Bolivia", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_BO *****/ /***** LOCALE BEGIN es_CL: Spanish - Chile *****/ -MY_LOCALE my_locale_es_CL ( "es_CL", "Spanish - Chile", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_CL +( + 83, + "es_CL", + "Spanish - Chile", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_CL *****/ /***** LOCALE BEGIN es_CO: Spanish - Columbia *****/ -MY_LOCALE my_locale_es_CO ( "es_CO", "Spanish - Columbia", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_CO +( + 84, + "es_CO", + "Spanish - Columbia", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_CO *****/ /***** LOCALE BEGIN es_CR: Spanish - Costa Rica *****/ -MY_LOCALE my_locale_es_CR ( "es_CR", "Spanish - Costa Rica", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_CR +( + 85, + "es_CR", + "Spanish - Costa Rica", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_CR *****/ /***** LOCALE BEGIN es_DO: Spanish - Dominican Republic *****/ -MY_LOCALE my_locale_es_DO ( "es_DO", "Spanish - Dominican Republic", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_DO +( + 86, + "es_DO", + "Spanish - Dominican Republic", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_DO *****/ /***** LOCALE BEGIN es_EC: Spanish - Ecuador *****/ -MY_LOCALE my_locale_es_EC ( "es_EC", "Spanish - Ecuador", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_EC +( + 87, + "es_EC", + "Spanish - Ecuador", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_EC *****/ /***** LOCALE BEGIN es_GT: Spanish - Guatemala *****/ -MY_LOCALE my_locale_es_GT ( "es_GT", "Spanish - Guatemala", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_GT +( + 88, + "es_GT", + "Spanish - Guatemala", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_GT *****/ /***** LOCALE BEGIN es_HN: Spanish - Honduras *****/ -MY_LOCALE my_locale_es_HN ( "es_HN", "Spanish - Honduras", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_HN +( + 89, + "es_HN", + "Spanish - Honduras", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_HN *****/ /***** LOCALE BEGIN es_MX: Spanish - Mexico *****/ -MY_LOCALE my_locale_es_MX ( "es_MX", "Spanish - Mexico", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_MX +( + 90, + "es_MX", + "Spanish - Mexico", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_MX *****/ /***** LOCALE BEGIN es_NI: Spanish - Nicaragua *****/ -MY_LOCALE my_locale_es_NI ( "es_NI", "Spanish - Nicaragua", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_NI +( + 91, + "es_NI", + "Spanish - Nicaragua", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_NI *****/ /***** LOCALE BEGIN es_PA: Spanish - Panama *****/ -MY_LOCALE my_locale_es_PA ( "es_PA", "Spanish - Panama", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_PA +( + 92, + "es_PA", + "Spanish - Panama", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_PA *****/ /***** LOCALE BEGIN es_PE: Spanish - Peru *****/ -MY_LOCALE my_locale_es_PE ( "es_PE", "Spanish - Peru", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_PE +( + 93, + "es_PE", + "Spanish - Peru", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_PE *****/ /***** LOCALE BEGIN es_PR: Spanish - Puerto Rico *****/ -MY_LOCALE my_locale_es_PR ( "es_PR", "Spanish - Puerto Rico", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_PR +( + 94, + "es_PR", + "Spanish - Puerto Rico", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_PR *****/ /***** LOCALE BEGIN es_PY: Spanish - Paraguay *****/ -MY_LOCALE my_locale_es_PY ( "es_PY", "Spanish - Paraguay", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_PY +( + 95, + "es_PY", + "Spanish - Paraguay", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_PY *****/ /***** LOCALE BEGIN es_SV: Spanish - El Salvador *****/ -MY_LOCALE my_locale_es_SV ( "es_SV", "Spanish - El Salvador", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_SV +( + 96, + "es_SV", + "Spanish - El Salvador", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_SV *****/ /***** LOCALE BEGIN es_US: Spanish - United States *****/ -MY_LOCALE my_locale_es_US ( "es_US", "Spanish - United States", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_US +( + 97, + "es_US", + "Spanish - United States", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_US *****/ /***** LOCALE BEGIN es_UY: Spanish - Uruguay *****/ -MY_LOCALE my_locale_es_UY ( "es_UY", "Spanish - Uruguay", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_UY +( + 98, + "es_UY", + "Spanish - Uruguay", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_UY *****/ /***** LOCALE BEGIN es_VE: Spanish - Venezuela *****/ -MY_LOCALE my_locale_es_VE ( "es_VE", "Spanish - Venezuela", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); +MY_LOCALE my_locale_es_VE +( + 99, + "es_VE", + "Spanish - Venezuela", + FALSE, + &my_locale_typelib_month_names_es_ES, + &my_locale_typelib_ab_month_names_es_ES, + &my_locale_typelib_day_names_es_ES, + &my_locale_typelib_ab_day_names_es_ES +); /***** LOCALE END es_VE *****/ /***** LOCALE BEGIN fr_BE: French - Belgium *****/ -MY_LOCALE my_locale_fr_BE ( "fr_BE", "French - Belgium", FALSE, &my_locale_typelib_month_names_fr_FR, &my_locale_typelib_ab_month_names_fr_FR, &my_locale_typelib_day_names_fr_FR, &my_locale_typelib_ab_day_names_fr_FR ); +MY_LOCALE my_locale_fr_BE +( + 100, + "fr_BE", + "French - Belgium", + FALSE, + &my_locale_typelib_month_names_fr_FR, + &my_locale_typelib_ab_month_names_fr_FR, + &my_locale_typelib_day_names_fr_FR, + &my_locale_typelib_ab_day_names_fr_FR +); /***** LOCALE END fr_BE *****/ /***** LOCALE BEGIN fr_CA: French - Canada *****/ -MY_LOCALE my_locale_fr_CA ( "fr_CA", "French - Canada", FALSE, &my_locale_typelib_month_names_fr_FR, &my_locale_typelib_ab_month_names_fr_FR, &my_locale_typelib_day_names_fr_FR, &my_locale_typelib_ab_day_names_fr_FR ); +MY_LOCALE my_locale_fr_CA +( + 101, + "fr_CA", + "French - Canada", + FALSE, + &my_locale_typelib_month_names_fr_FR, + &my_locale_typelib_ab_month_names_fr_FR, + &my_locale_typelib_day_names_fr_FR, + &my_locale_typelib_ab_day_names_fr_FR +); /***** LOCALE END fr_CA *****/ /***** LOCALE BEGIN fr_CH: French - Switzerland *****/ -MY_LOCALE my_locale_fr_CH ( "fr_CH", "French - Switzerland", FALSE, &my_locale_typelib_month_names_fr_FR, &my_locale_typelib_ab_month_names_fr_FR, &my_locale_typelib_day_names_fr_FR, &my_locale_typelib_ab_day_names_fr_FR ); +MY_LOCALE my_locale_fr_CH +( + 102, + "fr_CH", + "French - Switzerland", + FALSE, + &my_locale_typelib_month_names_fr_FR, + &my_locale_typelib_ab_month_names_fr_FR, + &my_locale_typelib_day_names_fr_FR, + &my_locale_typelib_ab_day_names_fr_FR +); /***** LOCALE END fr_CH *****/ /***** LOCALE BEGIN fr_LU: French - Luxembourg *****/ -MY_LOCALE my_locale_fr_LU ( "fr_LU", "French - Luxembourg", FALSE, &my_locale_typelib_month_names_fr_FR, &my_locale_typelib_ab_month_names_fr_FR, &my_locale_typelib_day_names_fr_FR, &my_locale_typelib_ab_day_names_fr_FR ); +MY_LOCALE my_locale_fr_LU +( + 103, + "fr_LU", + "French - Luxembourg", + FALSE, + &my_locale_typelib_month_names_fr_FR, + &my_locale_typelib_ab_month_names_fr_FR, + &my_locale_typelib_day_names_fr_FR, + &my_locale_typelib_ab_day_names_fr_FR +); /***** LOCALE END fr_LU *****/ /***** LOCALE BEGIN it_IT: Italian - Italy *****/ -MY_LOCALE my_locale_it_IT ( "it_IT", "Italian - Italy", FALSE, &my_locale_typelib_month_names_it_CH, &my_locale_typelib_ab_month_names_it_CH, &my_locale_typelib_day_names_it_CH, &my_locale_typelib_ab_day_names_it_CH ); +MY_LOCALE my_locale_it_IT +( + 104, + "it_IT", + "Italian - Italy", + FALSE, + &my_locale_typelib_month_names_it_CH, + &my_locale_typelib_ab_month_names_it_CH, + &my_locale_typelib_day_names_it_CH, + &my_locale_typelib_ab_day_names_it_CH +); /***** LOCALE END it_IT *****/ /***** LOCALE BEGIN nl_BE: Dutch - Belgium *****/ -MY_LOCALE my_locale_nl_BE ( "nl_BE", "Dutch - Belgium", TRUE, &my_locale_typelib_month_names_nl_NL, &my_locale_typelib_ab_month_names_nl_NL, &my_locale_typelib_day_names_nl_NL, &my_locale_typelib_ab_day_names_nl_NL ); +MY_LOCALE my_locale_nl_BE +( + 105, + "nl_BE", + "Dutch - Belgium", + TRUE, + &my_locale_typelib_month_names_nl_NL, + &my_locale_typelib_ab_month_names_nl_NL, + &my_locale_typelib_day_names_nl_NL, + &my_locale_typelib_ab_day_names_nl_NL +); /***** LOCALE END nl_BE *****/ /***** LOCALE BEGIN no_NO: Norwegian - Norway *****/ -MY_LOCALE my_locale_no_NO ( "no_NO", "Norwegian - Norway", FALSE, &my_locale_typelib_month_names_nb_NO, &my_locale_typelib_ab_month_names_nb_NO, &my_locale_typelib_day_names_nb_NO, &my_locale_typelib_ab_day_names_nb_NO ); +MY_LOCALE my_locale_no_NO +( + 106, + "no_NO", + "Norwegian - Norway", + FALSE, + &my_locale_typelib_month_names_nb_NO, + &my_locale_typelib_ab_month_names_nb_NO, + &my_locale_typelib_day_names_nb_NO, + &my_locale_typelib_ab_day_names_nb_NO +); /***** LOCALE END no_NO *****/ /***** LOCALE BEGIN sv_FI: Swedish - Finland *****/ -MY_LOCALE my_locale_sv_FI ( "sv_FI", "Swedish - Finland", FALSE, &my_locale_typelib_month_names_sv_SE, &my_locale_typelib_ab_month_names_sv_SE, &my_locale_typelib_day_names_sv_SE, &my_locale_typelib_ab_day_names_sv_SE ); +MY_LOCALE my_locale_sv_FI +( + 107, + "sv_FI", + "Swedish - Finland", + FALSE, + &my_locale_typelib_month_names_sv_SE, + &my_locale_typelib_ab_month_names_sv_SE, + &my_locale_typelib_day_names_sv_SE, + &my_locale_typelib_ab_day_names_sv_SE +); /***** LOCALE END sv_FI *****/ /***** LOCALE BEGIN zh_HK: Chinese - Hong Kong SAR *****/ -MY_LOCALE my_locale_zh_HK ( "zh_HK", "Chinese - Hong Kong SAR", FALSE, &my_locale_typelib_month_names_zh_CN, &my_locale_typelib_ab_month_names_zh_CN, &my_locale_typelib_day_names_zh_CN, &my_locale_typelib_ab_day_names_zh_CN ); +MY_LOCALE my_locale_zh_HK +( + 108, + "zh_HK", + "Chinese - Hong Kong SAR", + FALSE, + &my_locale_typelib_month_names_zh_CN, + &my_locale_typelib_ab_month_names_zh_CN, + &my_locale_typelib_day_names_zh_CN, + &my_locale_typelib_ab_day_names_zh_CN +); /***** LOCALE END zh_HK *****/ + +/* + The list of all locales. + Note, locales must be ordered according to their + numbers to make my_locale_by_number() work fast. + Some debug asserts below check this. +*/ MY_LOCALE *my_locales[]= { &my_locale_en_US, @@ -1496,3 +2581,31 @@ MY_LOCALE *my_locales[]= &my_locale_zh_HK, NULL }; + + +MY_LOCALE *my_locale_by_number(uint number) +{ + MY_LOCALE *locale; + if (number >= array_elements(my_locales) - 1) + return NULL; + locale= my_locales[number]; + // Check that locale is on its correct position in the array + DBUG_ASSERT(locale == my_locales[locale->number]); + return locale; +} + + +MY_LOCALE *my_locale_by_name(const char *name) +{ + MY_LOCALE **locale; + for (locale= my_locales; *locale != NULL; locale++) + { + if (!my_strcasecmp(&my_charset_latin1, (*locale)->name, name)) + { + // Check that locale is on its correct position in the array + DBUG_ASSERT((*locale) == my_locales[(*locale)->number]); + return *locale; + } + } + return NULL; +} diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc index b3c67ab5db7..b0ca7667a62 100644 --- a/sql/sql_manager.cc +++ b/sql/sql_manager.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000, 2002, 2005 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_map.cc b/sql/sql_map.cc index 8376b3bbfcc..36a47f1aefc 100644 --- a/sql/sql_map.cc +++ b/sql/sql_map.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2001, 2004-2005 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_map.h b/sql/sql_map.h index bfa6011ac54..d8eb64995aa 100644 --- a/sql/sql_map.h +++ b/sql/sql_map.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2001, 2005 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_olap.cc b/sql/sql_olap.cc index 3d06030536f..818825d566b 100644 --- a/sql/sql_olap.cc +++ b/sql/sql_olap.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 51fa86affc2..bfcbd4663b4 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -28,24 +27,7 @@ #include "sp_cache.h" #include "events.h" #include "event_data_objects.h" - -#ifdef HAVE_OPENSSL -/* - Without SSL the handshake consists of one packet. This packet - has both client capabilites and scrambled password. - With SSL the handshake might consist of two packets. If the first - packet (client capabilities) has CLIENT_SSL flag set, we have to - switch to SSL and read the second packet. The scrambled password - is in the second packet and client_capabilites field will be ignored. - Maybe it is better to accept flags other than CLIENT_SSL from the - second packet? -*/ -#define SSL_HANDSHAKE_SIZE 2 -#define NORMAL_HANDSHAKE_SIZE 6 -#define MIN_HANDSHAKE_SIZE 2 -#else -#define MIN_HANDSHAKE_SIZE 6 -#endif /* HAVE_OPENSSL */ +#include "sql_trigger.h" /* Used in error handling only */ #define SP_TYPE_STRING(LP) \ @@ -57,16 +39,6 @@ (LP)->sql_command == SQLCOM_DROP_FUNCTION ? \ "FUNCTION" : "PROCEDURE") -#ifdef SOLARIS -extern "C" int gethostname(char *name, int namelen); -#endif - -static void time_out_user_resource_limits(THD *thd, USER_CONN *uc); -#ifndef NO_EMBEDDED_ACCESS_CHECKS -static int check_for_max_user_connections(THD *thd, USER_CONN *uc); -static void decrease_user_connections(USER_CONN *uc); -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ -static bool check_multi_update_lock(THD *thd); static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables); const char *any_db="*any*"; // Special symbol for check_access @@ -109,20 +81,6 @@ const char *xa_state_names[]={ "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED" }; -#ifdef __WIN__ -static void test_signal(int sig_ptr) -{ -#if !defined( DBUG_OFF) - MessageBox(NULL,"Test signal","DBUG",MB_OK); -#endif -} -static void init_signals(void) -{ - int signals[7] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGBREAK,SIGABRT } ; - for (int i=0 ; i < 7 ; i++) - signal( signals[i], test_signal) ; -} -#endif static void unlock_locked_tables(THD *thd) { @@ -166,6 +124,7 @@ bool end_active_trans(THD *thd) DBUG_RETURN(error); } + bool begin_trans(THD *thd) { int error=0; @@ -217,409 +176,6 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables) return 0; } -#ifndef NO_EMBEDDED_ACCESS_CHECKS -static HASH hash_user_connections; - -static int get_or_create_user_conn(THD *thd, const char *user, - const char *host, - USER_RESOURCES *mqh) -{ - int return_val= 0; - uint temp_len, user_len; - char temp_user[USER_HOST_BUFF_SIZE]; - struct user_conn *uc; - - DBUG_ASSERT(user != 0); - DBUG_ASSERT(host != 0); - - user_len= strlen(user); - temp_len= (strmov(strmov(temp_user, user)+1, host) - temp_user)+1; - (void) pthread_mutex_lock(&LOCK_user_conn); - if (!(uc = (struct user_conn *) hash_search(&hash_user_connections, - (byte*) temp_user, temp_len))) - { - /* First connection for user; Create a user connection object */ - if (!(uc= ((struct user_conn*) - my_malloc(sizeof(struct user_conn) + temp_len+1, - MYF(MY_WME))))) - { - net_send_error(thd, 0, NullS); // Out of memory - return_val= 1; - goto end; - } - uc->user=(char*) (uc+1); - memcpy(uc->user,temp_user,temp_len+1); - uc->host= uc->user + user_len + 1; - uc->len= temp_len; - uc->connections= uc->questions= uc->updates= uc->conn_per_hour= 0; - uc->user_resources= *mqh; - uc->intime= thd->thr_create_time; - if (my_hash_insert(&hash_user_connections, (byte*) uc)) - { - my_free((char*) uc,0); - net_send_error(thd, 0, NullS); // Out of memory - return_val= 1; - goto end; - } - } - thd->user_connect=uc; - uc->connections++; -end: - (void) pthread_mutex_unlock(&LOCK_user_conn); - return return_val; - -} -#endif /* !NO_EMBEDDED_ACCESS_CHECKS */ - - -/* - Check if user exist and password supplied is correct. - - SYNOPSIS - check_user() - thd thread handle, thd->security_ctx->{host,user,ip} are used - command originator of the check: now check_user is called - during connect and change user procedures; used for - logging. - passwd scrambled password received from client - passwd_len length of scrambled password - db database name to connect to, may be NULL - check_count dont know exactly - - Note, that host, user and passwd may point to communication buffer. - Current implementation does not depend on that, but future changes - should be done with this in mind; 'thd' is INOUT, all other params - are 'IN'. - - RETURN VALUE - 0 OK; thd->security_ctx->user/master_access/priv_user/db_access and - thd->db are updated; OK is sent to client; - -1 access denied or handshake error; error is sent to client; - >0 error, not sent to client -*/ - -int check_user(THD *thd, enum enum_server_command command, - const char *passwd, uint passwd_len, const char *db, - bool check_count) -{ - DBUG_ENTER("check_user"); - -#ifdef NO_EMBEDDED_ACCESS_CHECKS - thd->main_security_ctx.master_access= GLOBAL_ACLS; // Full rights - /* Change database if necessary */ - if (db && db[0]) - { - /* - thd->db is saved in caller and needs to be freed by caller if this - function returns 0 - */ - thd->reset_db(NULL, 0); - if (mysql_change_db(thd, db, FALSE)) - { - /* Send the error to the client */ - net_send_error(thd); - DBUG_RETURN(-1); - } - } - send_ok(thd); - DBUG_RETURN(0); -#else - - my_bool opt_secure_auth_local; - pthread_mutex_lock(&LOCK_global_system_variables); - opt_secure_auth_local= opt_secure_auth; - pthread_mutex_unlock(&LOCK_global_system_variables); - - /* - If the server is running in secure auth mode, short scrambles are - forbidden. - */ - if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323) - { - net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE); - general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE)); - DBUG_RETURN(-1); - } - if (passwd_len != 0 && - passwd_len != SCRAMBLE_LENGTH && - passwd_len != SCRAMBLE_LENGTH_323) - DBUG_RETURN(ER_HANDSHAKE_ERROR); - - /* - Clear thd->db as it points to something, that will be freed when - connection is closed. We don't want to accidentally free a wrong pointer - if connect failed. Also in case of 'CHANGE USER' failure, current - database will be switched to 'no database selected'. - */ - thd->reset_db(NULL, 0); - - USER_RESOURCES ur; - int res= acl_getroot(thd, &ur, passwd, passwd_len); -#ifndef EMBEDDED_LIBRARY - if (res == -1) - { - /* - This happens when client (new) sends password scrambled with - scramble(), but database holds old value (scrambled with - scramble_323()). Here we please client to send scrambled_password - in old format. - */ - NET *net= &thd->net; - if (opt_secure_auth_local) - { - net_printf_error(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE, - thd->main_security_ctx.user, - thd->main_security_ctx.host_or_ip); - general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE), - thd->main_security_ctx.user, - thd->main_security_ctx.host_or_ip); - DBUG_RETURN(-1); - } - /* We have to read very specific packet size */ - if (send_old_password_request(thd) || - my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) - { - inc_host_errors(&thd->remote.sin_addr); - DBUG_RETURN(ER_HANDSHAKE_ERROR); - } - /* Final attempt to check the user based on reply */ - /* So as passwd is short, errcode is always >= 0 */ - res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323); - } -#endif /*EMBEDDED_LIBRARY*/ - /* here res is always >= 0 */ - if (res == 0) - { - if (!(thd->main_security_ctx.master_access & - NO_ACCESS)) // authentication is OK - { - DBUG_PRINT("info", - ("Capabilities: %lx packet_length: %ld Host: '%s' " - "Login user: '%s' Priv_user: '%s' Using password: %s " - "Access: %u db: '%s'", - thd->client_capabilities, - thd->max_client_packet_length, - thd->main_security_ctx.host_or_ip, - thd->main_security_ctx.user, - thd->main_security_ctx.priv_user, - passwd_len ? "yes": "no", - thd->main_security_ctx.master_access, - (thd->db ? thd->db : "*none*"))); - - if (check_count) - { - VOID(pthread_mutex_lock(&LOCK_thread_count)); - bool count_ok= thread_count <= max_connections + delayed_insert_threads - || (thd->main_security_ctx.master_access & SUPER_ACL); - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - if (!count_ok) - { // too many connections - net_send_error(thd, ER_CON_COUNT_ERROR); - DBUG_RETURN(-1); - } - } - - /* Why logging is performed before all checks've passed? */ - general_log_print(thd, command, - (thd->main_security_ctx.priv_user == - thd->main_security_ctx.user ? - (char*) "%s@%s on %s" : - (char*) "%s@%s as anonymous on %s"), - thd->main_security_ctx.user, - thd->main_security_ctx.host_or_ip, - db ? db : (char*) ""); - - /* - This is the default access rights for the current database. It's - set to 0 here because we don't have an active database yet (and we - may not have an active database to set. - */ - thd->main_security_ctx.db_access=0; - - /* Don't allow user to connect if he has done too many queries */ - if ((ur.questions || ur.updates || ur.conn_per_hour || ur.user_conn || - max_user_connections) && - get_or_create_user_conn(thd, - (opt_old_style_user_limits ? thd->main_security_ctx.user : - thd->main_security_ctx.priv_user), - (opt_old_style_user_limits ? thd->main_security_ctx.host_or_ip : - thd->main_security_ctx.priv_host), - &ur)) - DBUG_RETURN(-1); - if (thd->user_connect && - (thd->user_connect->user_resources.conn_per_hour || - thd->user_connect->user_resources.user_conn || - max_user_connections) && - check_for_max_user_connections(thd, thd->user_connect)) - DBUG_RETURN(-1); - - /* Change database if necessary */ - if (db && db[0]) - { - if (mysql_change_db(thd, db, FALSE)) - { - /* Send error to the client */ - net_send_error(thd); - if (thd->user_connect) - decrease_user_connections(thd->user_connect); - DBUG_RETURN(-1); - } - } - send_ok(thd); - thd->password= test(passwd_len); // remember for error messages - /* Ready to handle queries */ - DBUG_RETURN(0); - } - } - else if (res == 2) // client gave short hash, server has long hash - { - net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE); - general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE)); - DBUG_RETURN(-1); - } - net_printf_error(thd, ER_ACCESS_DENIED_ERROR, - thd->main_security_ctx.user, - thd->main_security_ctx.host_or_ip, - passwd_len ? ER(ER_YES) : ER(ER_NO)); - general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR), - thd->main_security_ctx.user, - thd->main_security_ctx.host_or_ip, - passwd_len ? ER(ER_YES) : ER(ER_NO)); - DBUG_RETURN(-1); -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ -} - -/* - Check for maximum allowable user connections, if the mysqld server is - started with corresponding variable that is greater then 0. -*/ - -extern "C" byte *get_key_conn(user_conn *buff, uint *length, - my_bool not_used __attribute__((unused))) -{ - *length=buff->len; - return (byte*) buff->user; -} - -extern "C" void free_user(struct user_conn *uc) -{ - my_free((char*) uc,MYF(0)); -} - -void init_max_user_conn(void) -{ -#ifndef NO_EMBEDDED_ACCESS_CHECKS - (void) hash_init(&hash_user_connections,system_charset_info,max_connections, - 0,0, - (hash_get_key) get_key_conn, (hash_free_key) free_user, - 0); -#endif -} - - -/* - check if user has already too many connections - - SYNOPSIS - check_for_max_user_connections() - thd Thread handle - uc User connect object - - NOTES - If check fails, we decrease user connection count, which means one - shouldn't call decrease_user_connections() after this function. - - RETURN - 0 ok - 1 error -*/ - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - -static int check_for_max_user_connections(THD *thd, USER_CONN *uc) -{ - int error=0; - DBUG_ENTER("check_for_max_user_connections"); - - (void) pthread_mutex_lock(&LOCK_user_conn); - if (max_user_connections && !uc->user_resources.user_conn && - max_user_connections < (uint) uc->connections) - { - net_printf_error(thd, ER_TOO_MANY_USER_CONNECTIONS, uc->user); - error=1; - goto end; - } - time_out_user_resource_limits(thd, uc); - if (uc->user_resources.user_conn && - uc->user_resources.user_conn < uc->connections) - { - net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, - "max_user_connections", - (long) uc->user_resources.user_conn); - error= 1; - goto end; - } - if (uc->user_resources.conn_per_hour && - uc->user_resources.conn_per_hour <= uc->conn_per_hour) - { - net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, - "max_connections_per_hour", - (long) uc->user_resources.conn_per_hour); - error=1; - goto end; - } - uc->conn_per_hour++; - - end: - if (error) - uc->connections--; // no need for decrease_user_connections() here - (void) pthread_mutex_unlock(&LOCK_user_conn); - DBUG_RETURN(error); -} - -/* - Decrease user connection count - - SYNOPSIS - decrease_user_connections() - uc User connection object - - NOTES - If there is a n user connection object for a connection - (which only happens if 'max_user_connections' is defined or - if someone has created a resource grant for a user), then - the connection count is always incremented on connect. - - The user connect object is not freed if some users has - 'max connections per hour' defined as we need to be able to hold - count over the lifetime of the connection. -*/ - -static void decrease_user_connections(USER_CONN *uc) -{ - DBUG_ENTER("decrease_user_connections"); - (void) pthread_mutex_lock(&LOCK_user_conn); - DBUG_ASSERT(uc->connections); - if (!--uc->connections && !mqh_used) - { - /* Last connection for user; Delete it */ - (void) hash_delete(&hash_user_connections,(byte*) uc); - } - (void) pthread_mutex_unlock(&LOCK_user_conn); - DBUG_VOID_RETURN; -} - -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ - - -void free_max_user_conn(void) -{ -#ifndef NO_EMBEDDED_ACCESS_CHECKS - hash_free(&hash_user_connections); -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ -} - - /* Mark all commands that somehow changes a table @@ -703,401 +259,6 @@ bool is_update_query(enum enum_sql_command command) return (sql_command_flags[command] & CF_CHANGES_DATA) != 0; } -/* - Reset per-hour user resource limits when it has been more than - an hour since they were last checked - - SYNOPSIS: - time_out_user_resource_limits() - thd Thread handler - uc User connection details - - NOTE: - This assumes that the LOCK_user_conn mutex has been acquired, so it is - safe to test and modify members of the USER_CONN structure. -*/ - -static void time_out_user_resource_limits(THD *thd, USER_CONN *uc) -{ - time_t check_time = thd->start_time ? thd->start_time : time(NULL); - DBUG_ENTER("time_out_user_resource_limits"); - - /* If more than a hour since last check, reset resource checking */ - if (check_time - uc->intime >= 3600) - { - uc->questions=1; - uc->updates=0; - uc->conn_per_hour=0; - uc->intime=check_time; - } - - DBUG_VOID_RETURN; -} - - -/* - Check if maximum queries per hour limit has been reached - returns 0 if OK. -*/ - -static bool check_mqh(THD *thd, uint check_command) -{ -#ifndef NO_EMBEDDED_ACCESS_CHECKS - bool error= 0; - USER_CONN *uc=thd->user_connect; - DBUG_ENTER("check_mqh"); - DBUG_ASSERT(uc != 0); - - (void) pthread_mutex_lock(&LOCK_user_conn); - - time_out_user_resource_limits(thd, uc); - - /* Check that we have not done too many questions / hour */ - if (uc->user_resources.questions && - uc->questions++ >= uc->user_resources.questions) - { - net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_questions", - (long) uc->user_resources.questions); - error=1; - goto end; - } - if (check_command < (uint) SQLCOM_END) - { - /* Check that we have not done too many updates / hour */ - if (uc->user_resources.updates && - (sql_command_flags[check_command] & CF_CHANGES_DATA) && - uc->updates++ >= uc->user_resources.updates) - { - net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_updates", - (long) uc->user_resources.updates); - error=1; - goto end; - } - } -end: - (void) pthread_mutex_unlock(&LOCK_user_conn); - DBUG_RETURN(error); -#else - return (0); -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ -} - - -static void reset_mqh(LEX_USER *lu, bool get_them= 0) -{ -#ifndef NO_EMBEDDED_ACCESS_CHECKS - (void) pthread_mutex_lock(&LOCK_user_conn); - if (lu) // for GRANT - { - USER_CONN *uc; - uint temp_len=lu->user.length+lu->host.length+2; - char temp_user[USER_HOST_BUFF_SIZE]; - - memcpy(temp_user,lu->user.str,lu->user.length); - memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length); - temp_user[lu->user.length]='\0'; temp_user[temp_len-1]=0; - if ((uc = (struct user_conn *) hash_search(&hash_user_connections, - (byte*) temp_user, temp_len))) - { - uc->questions=0; - get_mqh(temp_user,&temp_user[lu->user.length+1],uc); - uc->updates=0; - uc->conn_per_hour=0; - } - } - else - { - /* for FLUSH PRIVILEGES and FLUSH USER_RESOURCES */ - for (uint idx=0;idx < hash_user_connections.records; idx++) - { - USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections, - idx); - if (get_them) - get_mqh(uc->user,uc->host,uc); - uc->questions=0; - uc->updates=0; - uc->conn_per_hour=0; - } - } - (void) pthread_mutex_unlock(&LOCK_user_conn); -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ -} - -void thd_init_client_charset(THD *thd, uint cs_number) -{ - /* - Use server character set and collation if - - opt_character_set_client_handshake is not set - - client has not specified a character set - - client character set is the same as the servers - - client character set doesn't exists in server - */ - if (!opt_character_set_client_handshake || - !(thd->variables.character_set_client= get_charset(cs_number, MYF(0))) || - !my_strcasecmp(&my_charset_latin1, - global_system_variables.character_set_client->name, - thd->variables.character_set_client->name)) - { - thd->variables.character_set_client= - global_system_variables.character_set_client; - thd->variables.collation_connection= - global_system_variables.collation_connection; - thd->variables.character_set_results= - global_system_variables.character_set_results; - } - else - { - thd->variables.character_set_results= - thd->variables.collation_connection= - thd->variables.character_set_client; - } -} - - -/* - Perform handshake, authorize client and update thd ACL variables. - SYNOPSIS - check_connection() - thd thread handle - - RETURN - 0 success, OK is sent to user, thd is updated. - -1 error, which is sent to user - > 0 error code (not sent to user) -*/ - -#ifndef EMBEDDED_LIBRARY -static int check_connection(THD *thd) -{ - uint connect_errors= 0; - NET *net= &thd->net; - ulong pkt_len= 0; - char *end; - - DBUG_PRINT("info", - ("New connection received on %s", vio_description(net->vio))); -#ifdef SIGNAL_WITH_VIO_CLOSE - thd->set_active_vio(net->vio); -#endif - - if (!thd->main_security_ctx.host) // If TCP/IP connection - { - char ip[30]; - - if (vio_peer_addr(net->vio, ip, &thd->peer_port)) - return (ER_BAD_HOST_ERROR); - if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(0)))) - return (ER_OUT_OF_RESOURCES); - thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip; - vio_in_addr(net->vio,&thd->remote.sin_addr); - if (!(specialflag & SPECIAL_NO_RESOLVE)) - { - vio_in_addr(net->vio,&thd->remote.sin_addr); - thd->main_security_ctx.host= - ip_to_hostname(&thd->remote.sin_addr, &connect_errors); - /* Cut very long hostnames to avoid possible overflows */ - if (thd->main_security_ctx.host) - { - if (thd->main_security_ctx.host != my_localhost) - thd->main_security_ctx.host[min(strlen(thd->main_security_ctx.host), - HOSTNAME_LENGTH)]= 0; - thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host; - } - if (connect_errors > max_connect_errors) - return(ER_HOST_IS_BLOCKED); - } - DBUG_PRINT("info",("Host: %s ip: %s", - (thd->main_security_ctx.host ? - thd->main_security_ctx.host : "unknown host"), - (thd->main_security_ctx.ip ? - thd->main_security_ctx.ip : "unknown ip"))); - if (acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip)) - return(ER_HOST_NOT_PRIVILEGED); - } - else /* Hostname given means that the connection was on a socket */ - { - DBUG_PRINT("info",("Host: %s", thd->main_security_ctx.host)); - thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host; - thd->main_security_ctx.ip= 0; - /* Reset sin_addr */ - bzero((char*) &thd->remote, sizeof(thd->remote)); - } - vio_keepalive(net->vio, TRUE); - { - /* buff[] needs to big enough to hold the server_version variable */ - char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64]; - ulong client_flags = (CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB | - CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION); - - if (opt_using_transactions) - client_flags|=CLIENT_TRANSACTIONS; -#ifdef HAVE_COMPRESS - client_flags |= CLIENT_COMPRESS; -#endif /* HAVE_COMPRESS */ -#ifdef HAVE_OPENSSL - if (ssl_acceptor_fd) - client_flags |= CLIENT_SSL; /* Wow, SSL is available! */ -#endif /* HAVE_OPENSSL */ - - end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1; - int4store((uchar*) end, thd->thread_id); - end+= 4; - /* - So as check_connection is the only entry point to authorization - procedure, scramble is set here. This gives us new scramble for - each handshake. - */ - create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand); - /* - Old clients does not understand long scrambles, but can ignore packet - tail: that's why first part of the scramble is placed here, and second - part at the end of packet. - */ - end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1; - - int2store(end, client_flags); - /* write server characteristics: up to 16 bytes allowed */ - end[2]=(char) default_charset_info->number; - int2store(end+3, thd->server_status); - bzero(end+5, 13); - end+= 18; - /* write scramble tail */ - end= strmake(end, thd->scramble + SCRAMBLE_LENGTH_323, - SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323) + 1; - - /* At this point we write connection message and read reply */ - if (net_write_command(net, (uchar) protocol_version, "", 0, buff, - (uint) (end-buff)) || - (pkt_len= my_net_read(net)) == packet_error || - pkt_len < MIN_HANDSHAKE_SIZE) - { - inc_host_errors(&thd->remote.sin_addr); - return(ER_HANDSHAKE_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(ER_OUT_OF_RESOURCES); - - thd->client_capabilities=uint2korr(net->read_pos); - if (thd->client_capabilities & CLIENT_PROTOCOL_41) - { - thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16; - thd->max_client_packet_length= uint4korr(net->read_pos+4); - DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8])); - thd_init_client_charset(thd, (uint) net->read_pos[8]); - thd->update_charset(); - end= (char*) net->read_pos+32; - } - else - { - thd->max_client_packet_length= uint3korr(net->read_pos+2); - end= (char*) net->read_pos+5; - } - - if (thd->client_capabilities & CLIENT_IGNORE_SPACE) - thd->variables.sql_mode|= MODE_IGNORE_SPACE; -#ifdef HAVE_OPENSSL - DBUG_PRINT("info", ("client capabilities: %d", thd->client_capabilities)); - if (thd->client_capabilities & CLIENT_SSL) - { - /* Do the SSL layering. */ - if (!ssl_acceptor_fd) - { - inc_host_errors(&thd->remote.sin_addr); - return(ER_HANDSHAKE_ERROR); - } - DBUG_PRINT("info", ("IO layer change in progress...")); - if (sslaccept(ssl_acceptor_fd, net->vio, thd->variables.net_wait_timeout)) - { - DBUG_PRINT("error", ("Failed to accept new SSL connection")); - inc_host_errors(&thd->remote.sin_addr); - return(ER_HANDSHAKE_ERROR); - } - DBUG_PRINT("info", ("Reading user information over SSL layer")); - if ((pkt_len= my_net_read(net)) == packet_error || - pkt_len < NORMAL_HANDSHAKE_SIZE) - { - DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)", - pkt_len)); - inc_host_errors(&thd->remote.sin_addr); - return(ER_HANDSHAKE_ERROR); - } - } -#endif - - if (end >= (char*) net->read_pos+ pkt_len +2) - { - inc_host_errors(&thd->remote.sin_addr); - return(ER_HANDSHAKE_ERROR); - } - - if (thd->client_capabilities & CLIENT_INTERACTIVE) - thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout; - if ((thd->client_capabilities & CLIENT_TRANSACTIONS) && - opt_using_transactions) - net->return_status= &thd->server_status; - net->read_timeout=(uint) thd->variables.net_read_timeout; - - char *user= end; - char *passwd= strend(user)+1; - uint user_len= passwd - user - 1; - char *db= passwd; - char db_buff[NAME_LEN + 1]; // buffer to store db in utf8 - char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8 - uint dummy_errors; - - /* - Old clients send null-terminated string as password; new clients send - the size (1 byte) + string (not null-terminated). Hence in case of empty - password both send '\0'. - */ - uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? - *passwd++ : strlen(passwd); - db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ? - db + passwd_len + 1 : 0; - uint db_len= db ? strlen(db) : 0; - - if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len) - { - inc_host_errors(&thd->remote.sin_addr); - return ER_HANDSHAKE_ERROR; - } - - /* Since 4.1 all database names are stored in utf8 */ - if (db) - { - db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1, - system_charset_info, - db, db_len, - thd->charset(), &dummy_errors)]= 0; - db= db_buff; - } - - user_buff[user_len= copy_and_convert(user_buff, sizeof(user_buff)-1, - system_charset_info, user, user_len, - thd->charset(), &dummy_errors)]= '\0'; - user= user_buff; - - /* If username starts and ends in "'", chop them off */ - if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'') - { - user[user_len-1]= 0; - user++; - user_len-= 2; - } - - if (thd->main_security_ctx.user) - x_free(thd->main_security_ctx.user); - if (!(thd->main_security_ctx.user= my_strdup(user, MYF(0)))) - return (ER_OUT_OF_RESOURCES); - return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE); -} - void execute_init_command(THD *thd, sys_var_str *init_command_var, rw_lock_t *var_mutex) @@ -1130,138 +291,6 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var, } -pthread_handler_t handle_one_connection(void *arg) -{ - THD *thd=(THD*) arg; - uint launch_time = - (uint) ((thd->thr_create_time = time(NULL)) - thd->connect_time); - if (launch_time >= slow_launch_time) - statistic_increment(slow_launch_threads,&LOCK_status ); - - pthread_detach_this_thread(); - -#if !defined( __WIN__) // Win32 calls this in pthread_create - /* The following calls needs to be done before we call DBUG_ macros */ - if (!(test_flags & TEST_NO_THREADS) & my_thread_init()) - { - close_connection(thd, ER_OUT_OF_RESOURCES, 1); - statistic_increment(aborted_connects,&LOCK_status); - end_thread(thd,0); - return 0; - } -#endif - - /* - handle_one_connection() is the only way a thread would start - and would always be on top of the stack, therefore, the thread - stack always starts at the address of the first local variable - of handle_one_connection, which is thd. We need to know the - start of the stack so that we could check for stack overruns. - */ - DBUG_PRINT("info", ("handle_one_connection called by thread %d\n", - thd->thread_id)); - /* now that we've called my_thread_init(), it is safe to call DBUG_* */ - -#if defined(__WIN__) - init_signals(); -#elif !defined(__NETWARE__) - sigset_t set; - VOID(sigemptyset(&set)); // Get mask in use - VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); -#endif - thd->thread_stack= (char*) &thd; - if (thd->store_globals()) - { - close_connection(thd, ER_OUT_OF_RESOURCES, 1); - statistic_increment(aborted_connects,&LOCK_status); - end_thread(thd,0); - return 0; - } - - do - { - int error; - NET *net= &thd->net; - Security_context *sctx= thd->security_ctx; - net->no_send_error= 0; - - if ((error=check_connection(thd))) - { // Wrong permissions - if (error > 0) - net_printf_error(thd, error, sctx->host_or_ip); -#ifdef __NT__ - if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE) - my_sleep(1000); /* must wait after eof() */ -#endif - statistic_increment(aborted_connects,&LOCK_status); - goto end_thread; - } -#ifdef __NETWARE__ - netware_reg_user(sctx->ip, sctx->user, "MySQL"); -#endif - if (thd->variables.max_join_size == HA_POS_ERROR) - thd->options |= OPTION_BIG_SELECTS; - if (thd->client_capabilities & CLIENT_COMPRESS) - net->compress=1; // Use compression - - thd->version= refresh_version; - thd->proc_info= 0; - thd->command= COM_SLEEP; - thd->set_time(); - thd->init_for_queries(); - - if (sys_init_connect.value_length && !(sctx->master_access & SUPER_ACL)) - { - execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect); - if (thd->query_error) - thd->killed= THD::KILL_CONNECTION; - thd->proc_info=0; - thd->set_time(); - thd->init_for_queries(); - } - - while (!net->error && net->vio != 0 && - !(thd->killed == THD::KILL_CONNECTION)) - { - net->no_send_error= 0; - if (do_command(thd)) - break; - } - if (thd->user_connect) - decrease_user_connections(thd->user_connect); - if (net->error && net->vio != 0 && net->report_error) - { - if (!thd->killed && thd->variables.log_warnings > 1) - sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), - thd->thread_id,(thd->db ? thd->db : "unconnected"), - sctx->user ? sctx->user : "unauthenticated", - sctx->host_or_ip, - (net->last_errno ? ER(net->last_errno) : - ER(ER_UNKNOWN_ERROR))); - net_send_error(thd, net->last_errno, NullS); - statistic_increment(aborted_threads,&LOCK_status); - } - else if (thd->killed) - { - statistic_increment(aborted_threads,&LOCK_status); - } - -end_thread: - close_connection(thd, 0, 1); - end_thread(thd,1); - /* - If end_thread returns, we are either running with --one-thread - or this thread has been schedule to handle the next query - */ - thd= current_thd; - thd->thread_stack= (char*) &thd; - } while (!(test_flags & TEST_NO_THREADS)); - /* The following is only executed if we are not using --one-thread */ - return(0); /* purecov: deadcode */ -} - -#endif /* EMBEDDED_LIBRARY */ - /* Execute commands from bootstrap_file. Used when creating the initial grant tables @@ -1288,11 +317,6 @@ pthread_handler_t handle_bootstrap(void *arg) #ifndef EMBEDDED_LIBRARY pthread_detach_this_thread(); thd->thread_stack= (char*) &thd; -#if !defined(__WIN__) && !defined(__NETWARE__) - sigset_t set; - VOID(sigemptyset(&set)); // Get mask in use - VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); -#endif #endif /* EMBEDDED_LIBRARY */ if (thd->variables.max_join_size == HA_POS_ERROR) @@ -1314,51 +338,66 @@ pthread_handler_t handle_bootstrap(void *arg) thd->init_for_queries(); while (fgets(buff, thd->net.max_packet, file)) { - ulong length= (ulong) strlen(buff); - while (buff[length-1] != '\n' && !feof(file)) - { - /* - We got only a part of the current string. Will try to increase - net buffer then read the rest of the current string. - */ - if (net_realloc(&(thd->net), 2 * thd->net.max_packet)) - { - net_send_error(thd, ER_NET_PACKET_TOO_LARGE, NullS); - thd->fatal_error(); - break; - } - buff= (char*) thd->net.buff; - fgets(buff + length, thd->net.max_packet - length, file); - length+= (ulong) strlen(buff + length); - } - if (thd->is_fatal_error) - break; + /* strlen() can't be deleted because fgets() doesn't return length */ + ulong length= (ulong) strlen(buff); + while (buff[length-1] != '\n' && !feof(file)) + { + /* + We got only a part of the current string. Will try to increase + net buffer then read the rest of the current string. + */ + /* purecov: begin tested */ + if (net_realloc(&(thd->net), 2 * thd->net.max_packet)) + { + net_send_error(thd, ER_NET_PACKET_TOO_LARGE, NullS); + thd->fatal_error(); + break; + } + buff= (char*) thd->net.buff; + fgets(buff + length, thd->net.max_packet - length, file); + length+= (ulong) strlen(buff + length); + /* purecov: end */ + } + if (thd->is_fatal_error) + break; /* purecov: inspected */ while (length && (my_isspace(thd->charset(), buff[length-1]) || - buff[length-1] == ';')) + buff[length-1] == ';')) length--; buff[length]=0; thd->query_length=length; thd->query= thd->memdup_w_gap(buff, length+1, thd->db_length+1+QUERY_CACHE_FLAGS_SIZE); thd->query[length] = '\0'; + DBUG_PRINT("query",("%-.4096s",thd->query)); /* We don't need to obtain LOCK_thread_count here because in bootstrap mode we have only one thread. */ thd->query_id=next_query_id(); + thd->set_time(); mysql_parse(thd,thd->query,length); close_thread_tables(thd); // Free tables + if (thd->is_fatal_error) break; + + if (thd->net.report_error) + { + /* The query failed, send error to log and abort bootstrap */ + net_send_error(thd); + thd->fatal_error(); + break; + } + free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); #ifdef USING_TRANSACTIONS free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC)); #endif } - /* thd->fatal_error should be set in case something went wrong */ end: + /* Remember the exit code of bootstrap */ bootstrap_error= thd->is_fatal_error; net_end(&thd->net); @@ -1420,24 +459,30 @@ void cleanup_items(Item *item) */ static -int mysql_table_dump(THD* thd, char* db, char* tbl_name) +int mysql_table_dump(THD *thd, LEX_STRING *db, char *tbl_name) { TABLE* table; TABLE_LIST* table_list; int error = 0; DBUG_ENTER("mysql_table_dump"); - db = (db && db[0]) ? db : thd->db; + if (db->length == 0) + { + db->str= thd->db; /* purecov: inspected */ + db->length= thd->db_length; /* purecov: inspected */ + } if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST)))) DBUG_RETURN(1); // out of memory - table_list->db= db; + table_list->db= db->str; table_list->table_name= table_list->alias= tbl_name; table_list->lock_type= TL_READ_NO_INSERT; table_list->prev_global= &table_list; // can be removed after merge with 4.1 - if (!db || check_db_name(db)) + if (check_db_name(db)) { - my_error(ER_WRONG_DB_NAME ,MYF(0), db ? db : "NULL"); + /* purecov: begin inspected */ + my_error(ER_WRONG_DB_NAME ,MYF(0), db->str ? db->str : "NULL"); goto err; + /* purecov: end */ } if (lower_case_table_names) my_casedn_str(files_charset_info, tbl_name); @@ -1542,7 +587,7 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion) #ifndef EMBEDDED_LIBRARY /* - Read one command from socket and execute it (query or simple command). + Read one command from connection and execute it (query or simple command). This function is called in loop from thread function. SYNOPSIS do_command() @@ -1553,24 +598,26 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion) bool do_command(THD *thd) { - char *packet; - uint old_timeout; + char *packet= 0; ulong packet_length; - NET *net; + NET *net= &thd->net; enum enum_server_command command; DBUG_ENTER("do_command"); - net= &thd->net; /* indicator of uninitialized lex => normal flow of errors handling (see my_message_sql) */ thd->lex->current_select= 0; - packet=0; - old_timeout=net->read_timeout; - /* Wait max for 8 hours */ - net->read_timeout=(uint) thd->variables.net_wait_timeout; + /* + This thread will do a blocking read from the client which + will be interrupted when the next command is received from + the client, the connection is closed or "net_wait_timeout" + number of seconds has passed + */ + net_set_read_timeout(net, thd->variables.net_wait_timeout); + thd->clear_error(); // Clear error message net_new_transaction(net); @@ -1597,9 +644,12 @@ bool do_command(THD *thd) command= COM_END; // Wrong command DBUG_PRINT("info",("Command on %s = %d (%s)", vio_description(net->vio), command, - command_name[command])); + command_name[command].str)); } - net->read_timeout=old_timeout; // restore it + + /* Restore read timeout value */ + net_set_read_timeout(net, thd->variables.net_read_timeout); + /* packet_length contains length of data, as it was stored in packet header. In case of malformed header, packet_length can be zero. @@ -1650,7 +700,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */ thd->set_time(); VOID(pthread_mutex_lock(&LOCK_thread_count)); - thd->query_id=query_id; + thd->query_id= global_query_id; if (command != COM_STATISTICS && command != COM_PING) next_query_id(); thread_running++; @@ -1666,7 +716,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, statistic_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB], &LOCK_status); thd->convert_string(&tmp, system_charset_info, - packet, strlen(packet), thd->charset()); + packet, packet_length-1, thd->charset()); if (!mysql_change_db(thd, tmp.str, FALSE)) { general_log_print(thd, command, "%s",thd->db); @@ -1684,7 +734,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #endif case COM_TABLE_DUMP: { - char *db, *tbl_name; + char *tbl_name; + LEX_STRING db; uint db_len= *(uchar*) packet; if (db_len >= packet_length || db_len > NAME_LEN) { @@ -1700,34 +751,41 @@ bool dispatch_command(enum enum_server_command command, THD *thd, statistic_increment(thd->status_var.com_other, &LOCK_status); thd->enable_slow_log= opt_log_slow_admin_statements; - db= thd->alloc(db_len + tbl_len + 2); - if (!db) + db.str= thd->alloc(db_len + tbl_len + 2); + db.length= db_len; + if (!db.str) { my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); break; } - tbl_name= strmake(db, packet + 1, db_len)+1; + tbl_name= strmake(db.str, packet + 1, db_len)+1; strmake(tbl_name, packet + db_len + 2, tbl_len); - mysql_table_dump(thd, db, tbl_name); + mysql_table_dump(thd, &db, tbl_name); break; } case COM_CHANGE_USER: { + statistic_increment(thd->status_var.com_other, &LOCK_status); + char *user= (char*) packet, *packet_end= packet+ packet_length; + char *passwd= strend(user)+1; + thd->change_user(); thd->clear_error(); // if errors from rollback - statistic_increment(thd->status_var.com_other, &LOCK_status); - char *user= (char*) packet; - char *passwd= strend(user)+1; /* Old clients send null-terminated string ('\0' for empty string) for password. New clients send the size (1 byte) + string (not null terminated, so also '\0' for empty string). */ - char db_buff[NAME_LEN+1]; // buffer to store db in utf8 + char db_buff[NAME_LEN+1]; // buffer to store db in utf8 char *db= passwd; - uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? - *passwd++ : strlen(passwd); + char *save_db; + uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ? + *passwd++ : strlen(passwd)); + uint dummy_errors, save_db_length, db_length, res; + Security_context save_security_ctx= *thd->security_ctx; + USER_CONN *save_user_connect; + db+= passwd_len + 1; #ifndef EMBEDDED_LIBRARY /* Small check for incoming packet */ @@ -1738,17 +796,22 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } #endif /* Convert database name to utf8 */ - uint dummy_errors; + /* + Handle problem with old bug in client protocol where db had an extra + \0 + */ + db_length= (packet_end - db); + if (db_length > 0 && db[db_length-1] == 0) + db_length--; db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1, - system_charset_info, db, strlen(db), + system_charset_info, db, db_length, thd->charset(), &dummy_errors)]= 0; db= db_buff; /* Save user and privileges */ - uint save_db_length= thd->db_length; - char *save_db= thd->db; - Security_context save_security_ctx= *thd->security_ctx; - USER_CONN *save_user_connect= thd->user_connect; + save_db_length= thd->db_length; + save_db= thd->db; + save_user_connect= thd->user_connect; if (!(thd->security_ctx->user= my_strdup(user, MYF(0)))) { @@ -1759,7 +822,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, /* Clear variables that are allocated */ thd->user_connect= 0; - int res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, FALSE); + res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, FALSE); if (res) { @@ -1819,7 +882,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (alloc_query(thd, packet, packet_length)) break; // fatal error is set char *packet_end= thd->query + thd->query_length; - general_log_print(thd, command, "%.*b", thd->query_length, thd->query); + /* 'b' stands for 'buffer' parameter', special for 'my_snprintf' */ + const char *format= "%.*b"; + general_log_print(thd, command, format, thd->query_length, thd->query); DBUG_PRINT("query",("%-.4096s",thd->query)); if (!(specialflag & SPECIAL_NO_PRIOR)) @@ -1829,7 +894,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, while (!thd->killed && thd->lex->found_semicolon && !thd->net.report_error) { - char *packet= thd->lex->found_semicolon; + char *next_packet= thd->lex->found_semicolon; net->no_send_error= 0; /* Multiple queries exits, execute them individually @@ -1837,24 +902,24 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (thd->lock || thd->open_tables || thd->derived_tables || thd->prelocked_mode) close_thread_tables(thd); - ulong length= (ulong)(packet_end-packet); + ulong length= (ulong)(packet_end - next_packet); log_slow_statement(thd); /* Remove garbage at start of query */ - while (my_isspace(thd->charset(), *packet) && length > 0) + while (my_isspace(thd->charset(), *next_packet) && length > 0) { - packet++; + next_packet++; length--; } VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_length= length; - thd->query= packet; + thd->query= next_packet; thd->query_id= next_query_id(); thd->set_time(); /* Reset the query start time. */ /* TODO: set thd->lex->sql_command to SQLCOM_END here */ VOID(pthread_mutex_unlock(&LOCK_thread_count)); - mysql_parse(thd, packet, length); + mysql_parse(thd, next_packet, length); } if (!(specialflag & SPECIAL_NO_PRIOR)) @@ -1869,29 +934,28 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; #else { - char *fields, *pend; + char *fields, *packet_end= packet + packet_length - 1, *arg_end; /* Locked closure of all tables */ - TABLE_LIST *locked_tables= NULL; TABLE_LIST table_list; LEX_STRING conv_name; - /* Saved variable value */ - my_bool old_innodb_table_locks= thd->variables.innodb_table_locks; - + uint dummy; /* used as fields initializator */ lex_start(thd, 0, 0); - statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS], &LOCK_status); bzero((char*) &table_list,sizeof(table_list)); - if (thd->copy_db_to(&table_list.db, 0)) + if (thd->copy_db_to(&table_list.db, &dummy)) break; - pend= strend(packet); + /* + We have name + wildcard in packet, separated by endzero + */ + arg_end= strend(packet); thd->convert_string(&conv_name, system_charset_info, - packet, (uint) (pend-packet), thd->charset()); + packet, (uint) (arg_end - packet), thd->charset()); table_list.alias= table_list.table_name= conv_name.str; - packet= pend+1; + packet= arg_end + 1; if (!my_strcasecmp(system_charset_info, table_list.db, information_schema_name.str)) @@ -1901,7 +965,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, table_list.schema_table= schema_table; } - thd->query_length= strlen(packet); // for simplicity: don't optimize + thd->query_length= (uint) (packet_end - packet); // Don't count end \0 if (!(thd->query=fields=thd->memdup(packet,thd->query_length+1))) break; general_log_print(thd, command, "%s %s", table_list.table_name, fields); @@ -1937,24 +1001,27 @@ bool dispatch_command(enum enum_server_command command, THD *thd, error=TRUE; // End server break; +#ifdef REMOVED case COM_CREATE_DB: // QQ: To be removed { - char *db=thd->strdup(packet), *alias; + LEX_STRING db, alias; HA_CREATE_INFO create_info; statistic_increment(thd->status_var.com_stat[SQLCOM_CREATE_DB], &LOCK_status); - // null test to handle EOM - if (!db || !(alias= thd->strdup(db)) || check_db_name(db)) + if (thd->LEX_STRING_make(&db, packet, packet_length -1) || + thd->LEX_STRING_make(&alias, db.str, db.length) || + check_db_name(&db)) { - my_error(ER_WRONG_DB_NAME, MYF(0), db ? db : "NULL"); + my_error(ER_WRONG_DB_NAME, MYF(0), db.str ? db.str : "NULL"); break; } - if (check_access(thd,CREATE_ACL,db,0,1,0,is_schema_db(db))) + if (check_access(thd, CREATE_ACL, db.str , 0, 1, 0, + is_schema_db(db.str))) break; general_log_print(thd, command, packet); bzero(&create_info, sizeof(create_info)); - mysql_create_db(thd, (lower_case_table_names == 2 ? alias : db), + mysql_create_db(thd, (lower_case_table_names == 2 ? alias.str : db.str), &create_info, 0); break; } @@ -1962,14 +1029,15 @@ bool dispatch_command(enum enum_server_command command, THD *thd, { statistic_increment(thd->status_var.com_stat[SQLCOM_DROP_DB], &LOCK_status); - char *db=thd->strdup(packet); - /* null test to handle EOM */ - if (!db || check_db_name(db)) + LEX_STRING db; + + if (thd->LEX_STRING_make(&db, packet, packet_length - 1) || + check_db_name(&db)) { - my_error(ER_WRONG_DB_NAME, MYF(0), db ? db : "NULL"); + my_error(ER_WRONG_DB_NAME, MYF(0), db.str ? db.str : "NULL"); break; } - if (check_access(thd,DROP_ACL,db,0,1,0,is_schema_db(db))) + if (check_access(thd, DROP_ACL, db.str, 0, 1, 0, is_schema_db(db.str))) break; if (thd->locked_tables || thd->active_transaction()) { @@ -1977,10 +1045,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); break; } - general_log_print(thd, command, db); - mysql_rm_db(thd, db, 0, 0); + general_log_print(thd, command, db.str); + mysql_rm_db(thd, db.str, 0, 0); break; } +#endif #ifndef EMBEDDED_LIBRARY case COM_BINLOG_DUMP: { @@ -2038,7 +1107,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, */ enum mysql_enum_shutdown_level level= (enum mysql_enum_shutdown_level) (uchar) packet[0]; - DBUG_PRINT("quit",("Got shutdown command for level %u", level)); if (level == SHUTDOWN_DEFAULT) level= SHUTDOWN_WAIT_ALL_BUFFERS; // soon default will be configurable else if (level != SHUTDOWN_WAIT_ALL_BUFFERS) @@ -2052,7 +1120,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #ifdef __WIN__ sleep(1); // must wait after eof() #endif - send_eof(thd); // This is for 'quit request' + /* + The client is next going to send a COM_QUIT request (as part of + mysql_close()). Make the life simpler for the client by sending + the response for the coming COM_QUIT in advance + */ + send_eof(thd); close_connection(thd, 0, 1); close_thread_tables(thd); // Free before kill kill_mysql(); @@ -2062,37 +1135,47 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #endif case COM_STATISTICS: { - general_log_print(thd, command, NullS); - statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS], - &LOCK_status); + STATUS_VAR current_global_status_var; + ulong uptime; + uint length; #ifndef EMBEDDED_LIBRARY - char buff[200]; + char buff[250]; + uint buff_len= sizeof(buff); #else char *buff= thd->net.last_error; + uint buff_len= sizeof(thd->net.last_error); #endif - STATUS_VAR current_global_status_var; + general_log_print(thd, command, NullS); + statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS], + &LOCK_status); calc_sum_of_all_status(¤t_global_status_var); - - ulong uptime = (ulong) (thd->start_time - start_time); - sprintf((char*) buff, - "Uptime: %lu Threads: %d Questions: %lu Slow queries: %lu Opens: %lu Flush tables: %lu Open tables: %u Queries per second avg: %.3f", - uptime, - (int) thread_count, (ulong) thd->query_id, - current_global_status_var.long_query_count, - current_global_status_var.opened_tables, refresh_version, - cached_open_tables(), - (uptime ? (ulonglong2double(thd->query_id) / (double) uptime) : - (double) 0)); + uptime= (ulong) (thd->start_time - server_start_time); + length= my_snprintf((char*) buff, buff_len - 1, + "Uptime: %lu Threads: %d Questions: %lu " + "Slow queries: %lu Opens: %lu Flush tables: %lu " + "Open tables: %u Queries per second avg: %.3f", + uptime, + (int) thread_count, (ulong) thd->query_id, + current_global_status_var.long_query_count, + current_global_status_var.opened_tables, + refresh_version, + cached_open_tables(), + (uptime ? (ulonglong2double(thd->query_id) / + (double) uptime) : (double) 0)); #ifdef SAFEMALLOC if (sf_malloc_cur_memory) // Using SAFEMALLOC - sprintf(strend(buff), " Memory in use: %ldK Max memory used: %ldK", - (sf_malloc_cur_memory+1023L)/1024L, - (sf_malloc_max_memory+1023L)/1024L); + { + char *end= buff + length; + length+= my_snprintf(end, buff_len - length - 1, + end," Memory in use: %ldK Max memory used: %ldK", + (sf_malloc_cur_memory+1023L)/1024L, + (sf_malloc_max_memory+1023L)/1024L); + } #endif #ifndef EMBEDDED_LIBRARY - VOID(my_net_write(net, buff,(uint) strlen(buff))); - VOID(net_flush(net)); + VOID(my_net_write(net, buff, length)); + VOID(net_flush(net)); #endif break; } @@ -2122,13 +1205,14 @@ bool dispatch_command(enum enum_server_command command, THD *thd, { statistic_increment(thd->status_var.com_stat[SQLCOM_SET_OPTION], &LOCK_status); - enum_mysql_set_option command= (enum_mysql_set_option) uint2korr(packet); - switch (command) { - case MYSQL_OPTION_MULTI_STATEMENTS_ON: + uint opt_command= uint2korr(packet); + + switch (opt_command) { + case (int) MYSQL_OPTION_MULTI_STATEMENTS_ON: thd->client_capabilities|= CLIENT_MULTI_STATEMENTS; send_eof(thd); break; - case MYSQL_OPTION_MULTI_STATEMENTS_OFF: + case (int) MYSQL_OPTION_MULTI_STATEMENTS_OFF: thd->client_capabilities&= ~CLIENT_MULTI_STATEMENTS; send_eof(thd); break; @@ -2289,26 +1373,28 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, DBUG_RETURN(1); #else { - char *db; + LEX_STRING db; + uint dummy; if (lex->select_lex.db == NULL && - thd->copy_db_to(&lex->select_lex.db, 0)) + thd->copy_db_to(&lex->select_lex.db, &dummy)) { DBUG_RETURN(1); } - db= lex->select_lex.db; - if (check_db_name(db)) + db.str= lex->select_lex.db; + db.length= strlen(db.str); + if (check_db_name(&db)) { - my_error(ER_WRONG_DB_NAME, MYF(0), db); + my_error(ER_WRONG_DB_NAME, MYF(0), db.str); DBUG_RETURN(1); } - if (check_access(thd, SELECT_ACL, db, &thd->col_access, 0, 0, - is_schema_db(db))) + if (check_access(thd, SELECT_ACL, db.str, &thd->col_access, 0, 0, + is_schema_db(db.str))) DBUG_RETURN(1); /* purecov: inspected */ - if (!thd->col_access && check_grant_db(thd,db)) + if (!thd->col_access && check_grant_db(thd, db.str)) { my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), thd->security_ctx->priv_user, thd->security_ctx->priv_host, - db); + db.str); DBUG_RETURN(1); } break; @@ -2434,10 +1520,96 @@ static void reset_one_shot_variables(THD *thd) thd->update_charset(); thd->variables.time_zone= global_system_variables.time_zone; + thd->variables.lc_time_names= &my_locale_en_US; thd->one_shot_set= 0; } +static +bool sp_process_definer(THD *thd) +{ + DBUG_ENTER("sp_process_definer"); + + LEX *lex= thd->lex; + + /* + If the definer is not specified, this means that CREATE-statement missed + DEFINER-clause. DEFINER-clause can be missed in two cases: + + - The user submitted a statement w/o the clause. This is a normal + case, we should assign CURRENT_USER as definer. + + - Our slave received an updated from the master, that does not + replicate definer for stored rountines. We should also assign + CURRENT_USER as definer here, but also we should mark this routine + as NON-SUID. This is essential for the sake of backward + compatibility. + + The problem is the slave thread is running under "special" user (@), + that actually does not exist. In the older versions we do not fail + execution of a stored routine if its definer does not exist and + continue the execution under the authorization of the invoker + (BUG#13198). And now if we try to switch to slave-current-user (@), + we will fail. + + Actually, this leads to the inconsistent state of master and + slave (different definers, different SUID behaviour), but it seems, + this is the best we can do. + */ + + if (!lex->definer) + { + Query_arena original_arena; + Query_arena *ps_arena= thd->activate_stmt_arena_if_needed(&original_arena); + + lex->definer= create_default_definer(thd); + + if (ps_arena) + thd->restore_active_arena(ps_arena, &original_arena); + + /* Error has been already reported. */ + if (lex->definer == NULL) + DBUG_RETURN(TRUE); + + if (thd->slave_thread) + lex->sphead->m_chistics->suid= SP_IS_NOT_SUID; + } + else + { + /* + If the specified definer differs from the current user, we + should check that the current user has SUPER privilege (in order + to create a stored routine under another user one must have + SUPER privilege). + */ + if ((strcmp(lex->definer->user.str, thd->security_ctx->priv_user) || + my_strcasecmp(system_charset_info, lex->definer->host.str, + thd->security_ctx->priv_host)) && + check_global_access(thd, SUPER_ACL)) + { + my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); + DBUG_RETURN(TRUE); + } + } + + /* Check that the specified definer exists. Emit a warning if not. */ + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (!is_acl_user(lex->definer->host.str, lex->definer->user.str)) + { + push_warning_printf(thd, + MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_NO_SUCH_USER, + ER(ER_NO_SUCH_USER), + lex->definer->user.str, + lex->definer->host.str); + } +#endif /* NO_EMBEDDED_ACCESS_CHECKS */ + + DBUG_RETURN(FALSE); +} + + /* Execute command saved in thd and lex->sql_command @@ -2467,7 +1639,7 @@ mysql_execute_command(THD *thd) { bool res= FALSE; bool need_start_waiting= FALSE; // have protection against global read lock - int result= 0; + int up_result= 0; LEX *lex= thd->lex; /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ SELECT_LEX *select_lex= &lex->select_lex; @@ -2522,6 +1694,30 @@ mysql_execute_command(THD *thd) #ifdef HAVE_REPLICATION if (unlikely(thd->slave_thread)) { + if (lex->sql_command == SQLCOM_DROP_TRIGGER) + { + /* + When dropping a trigger, we need to load its table name + before checking slave filter rules. + */ + add_table_for_trigger(thd, thd->lex->spname, 1, &all_tables); + + if (!all_tables) + { + /* + If table name cannot be loaded, + it means the trigger does not exists possibly because + CREATE TRIGGER was previously skipped for this trigger + according to slave filtering rules. + Returning success without producing any errors in this case. + */ + DBUG_RETURN(0); + } + + // force searching in slave.cc:tables_ok() + all_tables->updating= 1; + } + /* Check if statment should be skipped because of slave filtering rules @@ -2573,10 +1769,11 @@ mysql_execute_command(THD *thd) if (opt_readonly && !(thd->security_ctx->master_access & SUPER_ACL) && (sql_command_flags[lex->sql_command] & CF_CHANGES_DATA) && - !((lex->sql_command == SQLCOM_CREATE_TABLE) && - (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) && - ((lex->sql_command != SQLCOM_UPDATE_MULTI) && - some_non_temp_table_to_be_updated(thd, all_tables))) + !((lex->sql_command == SQLCOM_CREATE_TABLE) && + (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) && + !((lex->sql_command == SQLCOM_DROP_TABLE) && lex->drop_temporary) && + ((lex->sql_command != SQLCOM_UPDATE_MULTI) && + some_non_temp_table_to_be_updated(thd, all_tables))) { my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); DBUG_RETURN(-1); @@ -2864,11 +2061,6 @@ mysql_execute_command(THD *thd) if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0)) goto error; } - if (strlen(first_table->table_name) > NAME_LEN) - { - my_error(ER_WRONG_TABLE_NAME, MYF(0), first_table->table_name); - break; - } pthread_mutex_lock(&LOCK_active_mi); /* fetch_master_table will send the error to the client on failure. @@ -2905,6 +2097,12 @@ mysql_execute_command(THD *thd) goto end_with_restore_list; #ifndef HAVE_READLINK + if (lex->create_info.data_file_name) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, + "DATA DIRECTORY option ignored"); + if (lex->create_info.index_file_name) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, + "INDEX DIRECTORY option ignored"); lex->create_info.data_file_name=lex->create_info.index_file_name=0; #else /* Fix names if symlinked tables */ @@ -2973,7 +2171,7 @@ mysql_execute_command(THD *thd) if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) { TABLE_LIST *duplicate; - if ((duplicate= unique_table(thd, create_table, select_tables))) + if ((duplicate= unique_table(thd, create_table, select_tables, 0))) { update_non_unique_table_error(create_table, "CREATE", duplicate); res= 1; @@ -2989,7 +2187,7 @@ mysql_execute_command(THD *thd) tab= tab->next_local) { TABLE_LIST *duplicate; - if ((duplicate= unique_table(thd, tab, select_tables))) + if ((duplicate= unique_table(thd, tab, select_tables, 0))) { update_non_unique_table_error(tab, "CREATE", duplicate); res= 1; @@ -3091,23 +2289,16 @@ end_with_restore_list: case SQLCOM_ALTER_TABLE: DBUG_ASSERT(first_table == all_tables && first_table != 0); -#if defined(DONT_ALLOW_SHOW_COMMANDS) - my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), - MYF(0)); /* purecov: inspected */ - goto error; -#else { ulong priv=0; ulong priv_needed= ALTER_ACL; - /* We also require DROP priv for ALTER TABLE ... DROP PARTITION */ - if (lex->alter_info.flags & ALTER_DROP_PARTITION) + /* + We also require DROP priv for ALTER TABLE ... DROP PARTITION, as well + as for RENAME TO, as being done by SQLCOM_RENAME_TABLE + */ + if (lex->alter_info.flags & (ALTER_DROP_PARTITION | ALTER_RENAME)) priv_needed|= DROP_ACL; - if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN)) - { - my_error(ER_WRONG_TABLE_NAME, MYF(0), lex->name); - goto error; - } /* Must be set in the parser */ DBUG_ASSERT(select_lex->db); if (check_access(thd, priv_needed, first_table->db, @@ -3123,11 +2314,11 @@ end_with_restore_list: { if (check_grant(thd, priv_needed, all_tables, 0, UINT_MAX, 0)) goto error; - if (lex->name && !test_all_bits(priv,INSERT_ACL | CREATE_ACL)) + if (lex->name.str && !test_all_bits(priv,INSERT_ACL | CREATE_ACL)) { // Rename of table TABLE_LIST tmp_table; bzero((char*) &tmp_table,sizeof(tmp_table)); - tmp_table.table_name=lex->name; + tmp_table.table_name= lex->name.str; tmp_table.db=select_lex->db; tmp_table.grant.privilege=priv; if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, 0, @@ -3155,7 +2346,7 @@ end_with_restore_list: } thd->enable_slow_log= opt_log_slow_admin_statements; - res= mysql_alter_table(thd, select_lex->db, lex->name, + res= mysql_alter_table(thd, select_lex->db, lex->name.str, &lex->create_info, first_table, lex->create_list, lex->key_list, @@ -3164,7 +2355,6 @@ end_with_restore_list: lex->ignore, &lex->alter_info, 1); break; } -#endif /*DONT_ALLOW_SHOW_COMMANDS*/ case SQLCOM_RENAME_TABLE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); @@ -3253,6 +2443,9 @@ end_with_restore_list: /* ! we write after unlocking the table */ if (!res && !lex->no_write_to_binlog) { + /* + Presumably, REPAIR and binlog writing doesn't require synchronization + */ if (mysql_bin_log.is_open()) { thd->clear_error(); // No binlog error generated @@ -3285,6 +2478,9 @@ end_with_restore_list: /* ! we write after unlocking the table */ if (!res && !lex->no_write_to_binlog) { + /* + Presumably, ANALYZE and binlog writing doesn't require synchronization + */ if (mysql_bin_log.is_open()) { thd->clear_error(); // No binlog error generated @@ -3309,6 +2505,9 @@ end_with_restore_list: /* ! we write after unlocking the table */ if (!res && !lex->no_write_to_binlog) { + /* + Presumably, OPTIMIZE and binlog writing doesn't require synchronization + */ if (mysql_bin_log.is_open()) { thd->clear_error(); // No binlog error generated @@ -3326,22 +2525,23 @@ end_with_restore_list: break; DBUG_ASSERT(select_lex->offset_limit == 0); unit->set_limit(select_lex); - res= (result= mysql_update(thd, all_tables, - select_lex->item_list, - lex->value_list, - select_lex->where, - select_lex->order_list.elements, - (ORDER *) select_lex->order_list.first, - unit->select_limit_cnt, - lex->duplicates, lex->ignore)); + res= (up_result= mysql_update(thd, all_tables, + select_lex->item_list, + lex->value_list, + select_lex->where, + select_lex->order_list.elements, + (ORDER *) select_lex->order_list.first, + unit->select_limit_cnt, + lex->duplicates, lex->ignore)); /* mysql_update return 2 if we need to switch to multi-update */ - if (result != 2) + if (up_result != 2) break; + /* Fall through */ case SQLCOM_UPDATE_MULTI: { DBUG_ASSERT(first_table == all_tables && first_table != 0); /* if we switched from normal update, rights are checked */ - if (result != 2) + if (up_result != 2) { if ((res= multi_update_precheck(thd, all_tables))) break; @@ -3410,15 +2610,23 @@ end_with_restore_list: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values, lex->update_list, lex->value_list, lex->duplicates, lex->ignore); - /* do not show last insert ID if VIEW does not have auto_inc */ + + /* + If we have inserted into a VIEW, and the base table has + AUTO_INCREMENT column, but this column is not accessible through + a view, then we should restore LAST_INSERT_ID to the value it + had before the statement. + */ if (first_table->view && !first_table->contain_auto_increment) - thd->first_successful_insert_id_in_cur_stmt= 0; + thd->first_successful_insert_id_in_cur_stmt= + thd->first_successful_insert_id_in_prev_stmt; + break; } case SQLCOM_REPLACE_SELECT: case SQLCOM_INSERT_SELECT: { - select_result *result; + select_result *sel_result; DBUG_ASSERT(first_table == all_tables && first_table != 0); if ((res= insert_precheck(thd, all_tables))) break; @@ -3447,13 +2655,15 @@ end_with_restore_list: select_lex->context.table_list= select_lex->context.first_name_resolution_table= second_table; res= mysql_insert_select_prepare(thd); - if (!res && (result= new select_insert(first_table, first_table->table, - &lex->field_list, - &lex->update_list, - &lex->value_list, - lex->duplicates, lex->ignore))) + if (!res && (sel_result= new select_insert(first_table, + first_table->table, + &lex->field_list, + &lex->update_list, + &lex->value_list, + lex->duplicates, + lex->ignore))) { - res= handle_select(thd, lex, result, OPTION_SETUP_TABLES_DONE); + res= handle_select(thd, lex, sel_result, OPTION_SETUP_TABLES_DONE); /* Invalidate the table in the query cache if something changed after unlocking when changes become visible. @@ -3463,18 +2673,30 @@ end_with_restore_list: if (first_table->lock_type == TL_WRITE_CONCURRENT_INSERT && thd->lock) { + /* INSERT ... SELECT should invalidate only the very first table */ + TABLE_LIST *save_table= first_table->next_local; + first_table->next_local= 0; mysql_unlock_tables(thd, thd->lock); query_cache_invalidate3(thd, first_table, 1); + first_table->next_local= save_table; thd->lock=0; } - delete result; + delete sel_result; } /* revert changes for SP */ select_lex->table_list.first= (byte*) first_table; } - /* do not show last insert ID if VIEW does not have auto_inc */ + + /* + If we have inserted into a VIEW, and the base table has + AUTO_INCREMENT column, but this column is not accessible through + a view, then we should restore LAST_INSERT_ID to the value it + had before the statement. + */ if (first_table->view && !first_table->contain_auto_increment) - thd->first_successful_insert_id_in_cur_stmt= 0; + thd->first_successful_insert_id_in_cur_stmt= + thd->first_successful_insert_id_in_prev_stmt; + break; } case SQLCOM_TRUNCATE: @@ -3484,7 +2706,7 @@ end_with_restore_list: break; } DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_one_table_access(thd, DELETE_ACL, all_tables)) + if (check_one_table_access(thd, DROP_ACL, all_tables)) goto error; /* Don't allow this within a transaction because we want to use @@ -3525,7 +2747,7 @@ end_with_restore_list: DBUG_ASSERT(first_table == all_tables && first_table != 0); TABLE_LIST *aux_tables= (TABLE_LIST *)thd->lex->auxiliary_table_list.first; - multi_delete *result; + multi_delete *del_result; if (!thd->locked_tables && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) @@ -3550,8 +2772,8 @@ end_with_restore_list: if ((res= mysql_multi_delete_prepare(thd))) goto error; - if (!thd->is_fatal_error && (result= new multi_delete(aux_tables, - lex->table_count))) + if (!thd->is_fatal_error && + (del_result= new multi_delete(aux_tables, lex->table_count))) { res= mysql_select(thd, &select_lex->ref_pointer_array, select_lex->get_table_list(), @@ -3563,8 +2785,8 @@ end_with_restore_list: select_lex->options | thd->options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE, - result, unit, select_lex); - delete result; + del_result, unit, select_lex); + delete del_result; } else res= TRUE; // Error @@ -3596,6 +2818,7 @@ end_with_restore_list: /* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */ thd->options|= OPTION_KEEP_LOG; } + /* DDL and binlog write order protected by LOCK_open */ res= mysql_rm_table(thd, first_table, lex->drop_if_exists, lex->drop_temporary); } @@ -3748,9 +2971,10 @@ end_with_restore_list: break; } char *alias; - if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name)) + if (!(alias=thd->strmake(lex->name.str, lex->name.length)) || + check_db_name(&lex->name)) { - my_error(ER_WRONG_DB_NAME, MYF(0), lex->name); + my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str); break; } /* @@ -3762,17 +2986,18 @@ end_with_restore_list: */ #ifdef HAVE_REPLICATION if (thd->slave_thread && - (!rpl_filter->db_ok(lex->name) || - !rpl_filter->db_ok_with_wild_table(lex->name))) + (!rpl_filter->db_ok(lex->name.str) || + !rpl_filter->db_ok_with_wild_table(lex->name.str))) { my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); break; } #endif - if (check_access(thd,CREATE_ACL,lex->name,0,1,0,is_schema_db(lex->name))) + if (check_access(thd,CREATE_ACL,lex->name.str, 0, 1, 0, + is_schema_db(lex->name.str))) break; - res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias : lex->name), - &lex->create_info, 0); + res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias : + lex->name.str), &lex->create_info, 0); break; } case SQLCOM_DROP_DB: @@ -3782,9 +3007,9 @@ end_with_restore_list: res= -1; break; } - if (check_db_name(lex->name)) + if (check_db_name(&lex->name)) { - my_error(ER_WRONG_DB_NAME, MYF(0), lex->name); + my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str); break; } /* @@ -3796,14 +3021,15 @@ end_with_restore_list: */ #ifdef HAVE_REPLICATION if (thd->slave_thread && - (!rpl_filter->db_ok(lex->name) || - !rpl_filter->db_ok_with_wild_table(lex->name))) + (!rpl_filter->db_ok(lex->name.str) || + !rpl_filter->db_ok_with_wild_table(lex->name.str))) { my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); break; } #endif - if (check_access(thd,DROP_ACL,lex->name,0,1,0,is_schema_db(lex->name))) + if (check_access(thd,DROP_ACL,lex->name.str,0,1,0, + is_schema_db(lex->name.str))) break; if (thd->locked_tables || thd->active_transaction()) { @@ -3811,7 +3037,7 @@ end_with_restore_list: ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); goto error; } - res= mysql_rm_db(thd, lex->name, lex->drop_if_exists, 0); + res= mysql_rm_db(thd, lex->name.str, lex->drop_if_exists, 0); break; } case SQLCOM_RENAME_DB: @@ -3837,6 +3063,11 @@ end_with_restore_list: break; } #endif + if (check_db_name(newdb)) + { + my_error(ER_WRONG_DB_NAME, MYF(0), newdb->str); + break; + } if (check_access(thd,ALTER_ACL,olddb->str,0,1,0,is_schema_db(olddb->str)) || check_access(thd,DROP_ACL,olddb->str,0,1,0,is_schema_db(olddb->str)) || check_access(thd,CREATE_ACL,newdb->str,0,1,0,is_schema_db(newdb->str))) @@ -3858,11 +3089,10 @@ end_with_restore_list: } case SQLCOM_ALTER_DB: { - char *db= lex->name; - DBUG_ASSERT(db); /* Must be set in the parser */ - if (!strip_sp(db) || check_db_name(db)) + LEX_STRING *db= &lex->name; + if (check_db_name(db)) { - my_error(ER_WRONG_DB_NAME, MYF(0), db); + my_error(ER_WRONG_DB_NAME, MYF(0), db->str); break; } /* @@ -3874,14 +3104,14 @@ end_with_restore_list: */ #ifdef HAVE_REPLICATION if (thd->slave_thread && - (!rpl_filter->db_ok(db) || - !rpl_filter->db_ok_with_wild_table(db))) + (!rpl_filter->db_ok(db->str) || + !rpl_filter->db_ok_with_wild_table(db->str))) { my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); break; } #endif - if (check_access(thd, ALTER_ACL, db, 0, 1, 0, is_schema_db(db))) + if (check_access(thd, ALTER_ACL, db->str, 0, 1, 0, is_schema_db(db->str))) break; if (thd->locked_tables || thd->active_transaction()) { @@ -3889,23 +3119,35 @@ end_with_restore_list: ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); goto error; } - res= mysql_alter_db(thd, db, &lex->create_info); + res= mysql_alter_db(thd, db->str, &lex->create_info); break; } case SQLCOM_SHOW_CREATE_DB: { - if (!strip_sp(lex->name) || check_db_name(lex->name)) + if (check_db_name(&lex->name)) { - my_error(ER_WRONG_DB_NAME, MYF(0), lex->name); + my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str); break; } - res=mysqld_show_create_db(thd,lex->name,&lex->create_info); + res= mysqld_show_create_db(thd, lex->name.str, &lex->create_info); break; } case SQLCOM_CREATE_EVENT: case SQLCOM_ALTER_EVENT: + do { DBUG_ASSERT(lex->event_parse_data); + if (lex->table_or_sp_used()) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored " + "function calls as part of this statement"); + break; + } + + res= sp_process_definer(thd); + if (res) + break; + switch (lex->sql_command) { case SQLCOM_CREATE_EVENT: res= Events::get_instance()-> @@ -3923,16 +3165,15 @@ end_with_restore_list: if (!res) send_ok(thd); - /* Don't do it, if we are inside a SP */ - if (!thd->spcont) - { - delete lex->sphead; - lex->sphead= NULL; - } - - /* lex->unit.cleanup() is called outside, no need to call it here */ - break; + } while (0); + /* Don't do it, if we are inside a SP */ + if (!thd->spcont) + { + delete lex->sphead; + lex->sphead= NULL; } + /* lex->unit.cleanup() is called outside, no need to call it here */ + break; case SQLCOM_DROP_EVENT: case SQLCOM_SHOW_CREATE_EVENT: { @@ -3958,12 +3199,10 @@ end_with_restore_list: lex->spname->m_name); else { - uint affected= 1; if (!(res= Events::get_instance()->drop_event(thd, lex->spname->m_db, lex->spname->m_name, - lex->drop_if_exists, - FALSE))) + lex->drop_if_exists))) send_ok(thd); } break; @@ -3995,13 +3234,9 @@ end_with_restore_list: break; if (end_active_trans(thd)) goto error; + /* Conditionally writes to binlog */ if (!(res= mysql_create_user(thd, lex->users_list))) - { - if (mysql_bin_log.is_open()) - thd->binlog_query(THD::MYSQL_QUERY_TYPE, - thd->query, thd->query_length, FALSE, FALSE); send_ok(thd); - } break; } case SQLCOM_DROP_USER: @@ -4011,15 +3246,9 @@ end_with_restore_list: break; if (end_active_trans(thd)) goto error; + /* Conditionally writes to binlog */ if (!(res= mysql_drop_user(thd, lex->users_list))) - { - if (mysql_bin_log.is_open()) - { - thd->binlog_query(THD::MYSQL_QUERY_TYPE, - thd->query, thd->query_length, FALSE, FALSE); - } send_ok(thd); - } break; } case SQLCOM_RENAME_USER: @@ -4029,15 +3258,9 @@ end_with_restore_list: break; if (end_active_trans(thd)) goto error; + /* Conditionally writes to binlog */ if (!(res= mysql_rename_user(thd, lex->users_list))) - { - if (mysql_bin_log.is_open()) - { - thd->binlog_query(THD::MYSQL_QUERY_TYPE, - thd->query, thd->query_length, FALSE, FALSE); - } send_ok(thd); - } break; } case SQLCOM_REVOKE_ALL: @@ -4045,15 +3268,9 @@ end_with_restore_list: if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) && check_global_access(thd,CREATE_USER_ACL)) break; + /* Conditionally writes to binlog */ if (!(res = mysql_revoke_all(thd, lex->users_list))) - { - if (mysql_bin_log.is_open()) - { - thd->binlog_query(THD::MYSQL_QUERY_TYPE, - thd->query, thd->query_length, FALSE, FALSE); - } send_ok(thd); - } break; } case SQLCOM_REVOKE: @@ -4112,6 +3329,7 @@ end_with_restore_list: check_grant_routine(thd, grants | GRANT_ACL, all_tables, lex->type == TYPE_ENUM_PROCEDURE, 0)) goto error; + /* Conditionally writes to binlog */ res= mysql_routine_grant(thd, all_tables, lex->type == TYPE_ENUM_PROCEDURE, lex->users_list, grants, @@ -4124,16 +3342,11 @@ end_with_restore_list: GRANT_ACL), all_tables, 0, UINT_MAX, 0)) goto error; + /* Conditionally writes to binlog */ res= mysql_table_grant(thd, all_tables, lex->users_list, lex->columns, lex->grant, lex->sql_command == SQLCOM_REVOKE); } - if (!res && mysql_bin_log.is_open()) - { - thd->clear_error(); - thd->binlog_query(THD::MYSQL_QUERY_TYPE, - thd->query, thd->query_length, FALSE, FALSE); - } } else { @@ -4144,16 +3357,11 @@ end_with_restore_list: goto error; } else + /* Conditionally writes to binlog */ res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant, lex->sql_command == SQLCOM_REVOKE); if (!res) { - if (mysql_bin_log.is_open()) - { - thd->clear_error(); - thd->binlog_query(THD::MYSQL_QUERY_TYPE, - thd->query, thd->query_length, FALSE, FALSE); - } if (lex->sql_command == SQLCOM_GRANT) { List_iterator <LEX_USER> str_list(lex->users_list); @@ -4162,7 +3370,7 @@ end_with_restore_list: { if (!(user= get_current_user(thd, tmp_user))) goto error; - reset_mqh(user); + reset_mqh(user, 0); } } } @@ -4181,6 +3389,7 @@ end_with_restore_list: bool write_to_binlog; if (check_global_access(thd,RELOAD_ACL)) goto error; + /* reload_acl_and_cache() will tell us if we are allowed to write to the binlog or not. @@ -4191,6 +3400,9 @@ end_with_restore_list: We WANT to write and we CAN write. ! we write after unlocking the table. */ + /* + Presumably, RESET and binlog writing doesn't require synchronization + */ if (!lex->no_write_to_binlog && write_to_binlog) { if (mysql_bin_log.is_open()) @@ -4200,13 +3412,21 @@ end_with_restore_list: } } send_ok(thd); - } + } + break; } case SQLCOM_KILL: { Item *it= (Item *)lex->value_list.head(); + if (lex->table_or_sp_used()) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored " + "function calls as part of this statement"); + break; + } + if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1)) { my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY), @@ -4378,26 +3598,38 @@ end_with_restore_list: { uint namelen; char *name; - int result; + int sp_result= SP_INTERNAL_ERROR; DBUG_ASSERT(lex->sphead != 0); DBUG_ASSERT(lex->sphead->m_db.str); /* Must be initialized in the parser */ - - if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str, 0, 0, 0, - is_schema_db(lex->sphead->m_db.str))) + /* + Verify that the database name is allowed, optionally + lowercase it. + */ + if (check_db_name(&lex->sphead->m_db)) { - delete lex->sphead; - lex->sphead= 0; - goto error; + my_error(ER_WRONG_DB_NAME, MYF(0), lex->sphead->m_db.str); + goto create_sp_error; } - if (end_active_trans(thd)) + /* + Check that a database directory with this name + exists. Design note: This won't work on virtual databases + like information_schema. + */ + if (check_db_dir_existence(lex->sphead->m_db.str)) { - delete lex->sphead; - lex->sphead= 0; - goto error; + my_error(ER_BAD_DB_ERROR, MYF(0), lex->sphead->m_db.str); + goto create_sp_error; } + if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str, 0, 0, 0, + is_schema_db(lex->sphead->m_db.str))) + goto create_sp_error; + + if (end_active_trans(thd)) + goto create_sp_error; + name= lex->sphead->name(&namelen); #ifdef HAVE_DLOPEN if (lex->sphead->m_type == TYPE_ENUM_FUNCTION) @@ -4406,147 +3638,58 @@ end_with_restore_list: if (udf) { - my_error(ER_UDF_EXISTS, MYF(0), name); - delete lex->sphead; - lex->sphead= 0; - goto error; + my_error(ER_UDF_EXISTS, MYF(0), name); + goto create_sp_error; } } #endif - /* - If the definer is not specified, this means that CREATE-statement missed - DEFINER-clause. DEFINER-clause can be missed in two cases: - - - The user submitted a statement w/o the clause. This is a normal - case, we should assign CURRENT_USER as definer. - - - Our slave received an updated from the master, that does not - replicate definer for stored rountines. We should also assign - CURRENT_USER as definer here, but also we should mark this routine - as NON-SUID. This is essential for the sake of backward - compatibility. - - The problem is the slave thread is running under "special" user (@), - that actually does not exist. In the older versions we do not fail - execution of a stored routine if its definer does not exist and - continue the execution under the authorization of the invoker - (BUG#13198). And now if we try to switch to slave-current-user (@), - we will fail. - - Actually, this leads to the inconsistent state of master and - slave (different definers, different SUID behaviour), but it seems, - this is the best we can do. - */ + if (sp_process_definer(thd)) + goto create_sp_error; - if (!lex->definer) - { - bool res= FALSE; - Query_arena original_arena; - Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena); - - if (!(lex->definer= create_default_definer(thd))) - res= TRUE; - - if (ps_arena) - thd->restore_active_arena(ps_arena, &original_arena); - - if (res) - { - /* Error has been already reported. */ - delete lex->sphead; - lex->sphead= 0; - goto error; - } - - if (thd->slave_thread) - lex->sphead->m_chistics->suid= SP_IS_NOT_SUID; - } - - /* - If the specified definer differs from the current user, we should check - that the current user has SUPER privilege (in order to create a stored - routine under another user one must have SUPER privilege). - */ - - else if (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) || - my_strcasecmp(system_charset_info, - lex->definer->host.str, - thd->security_ctx->priv_host)) - { - if (check_global_access(thd, SUPER_ACL)) - { - my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); - delete lex->sphead; - lex->sphead= 0; - goto error; - } - } - - /* Check that the specified definer exists. Emit a warning if not. */ - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - if (!is_acl_user(lex->definer->host.str, - lex->definer->user.str)) - { - push_warning_printf(thd, - MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_NO_SUCH_USER, - ER(ER_NO_SUCH_USER), - lex->definer->user.str, - lex->definer->host.str); - } -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ - - res= (result= lex->sphead->create(thd)); - if (result == SP_OK) - { + res= (sp_result= lex->sphead->create(thd)); + switch (sp_result) { + case SP_OK: #ifndef NO_EMBEDDED_ACCESS_CHECKS /* only add privileges if really neccessary */ if (sp_automatic_privileges && !opt_noacl && check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS, - lex->sphead->m_db.str, name, + lex->sphead->m_db.str, name, lex->sql_command == SQLCOM_CREATE_PROCEDURE, 1)) { if (sp_grant_privileges(thd, lex->sphead->m_db.str, name, lex->sql_command == SQLCOM_CREATE_PROCEDURE)) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_PROC_AUTO_GRANT_FAIL, - ER(ER_PROC_AUTO_GRANT_FAIL)); + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_PROC_AUTO_GRANT_FAIL, + ER(ER_PROC_AUTO_GRANT_FAIL)); close_thread_tables(thd); } #endif - lex->unit.cleanup(); - delete lex->sphead; - lex->sphead= 0; - send_ok(thd); - } - else - { - switch (result) { - case SP_WRITE_ROW_FAILED: - my_error(ER_SP_ALREADY_EXISTS, MYF(0), SP_TYPE_STRING(lex), name); - break; - case SP_NO_DB_ERROR: - my_error(ER_BAD_DB_ERROR, MYF(0), lex->sphead->m_db.str); - break; - case SP_BAD_IDENTIFIER: - my_error(ER_TOO_LONG_IDENT, MYF(0), name); - break; - case SP_BODY_TOO_LONG: - my_error(ER_TOO_LONG_BODY, MYF(0), name); - break; - default: - my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(lex), name); - break; - } - lex->unit.cleanup(); - delete lex->sphead; - lex->sphead= 0; - goto error; - } break; - } + case SP_WRITE_ROW_FAILED: + my_error(ER_SP_ALREADY_EXISTS, MYF(0), SP_TYPE_STRING(lex), name); + break; + case SP_BAD_IDENTIFIER: + my_error(ER_TOO_LONG_IDENT, MYF(0), name); + break; + case SP_BODY_TOO_LONG: + my_error(ER_TOO_LONG_BODY, MYF(0), name); + break; + default: + my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(lex), name); + break; + } /* end switch */ + + /* + Capture all errors within this CASE and + clean up the environment. + */ +create_sp_error: + if (sp_result != SP_OK ) + goto error; + send_ok(thd); + break; /* break super switch */ + } /* end case group bracket */ case SQLCOM_CALL: { sp_head *sp; @@ -4622,8 +3765,6 @@ end_with_restore_list: select_limit= thd->variables.select_limit; thd->variables.select_limit= HA_POS_ERROR; - thd->row_count_func= 0; - /* We never write CALL statements into binlog: - If the mode is non-prelocked, each statement will be logged @@ -4649,14 +3790,17 @@ end_with_restore_list: send_ok(thd, (ulong) (thd->row_count_func < 0 ? 0 : thd->row_count_func)); else + { + DBUG_ASSERT(thd->net.report_error == 1 || thd->killed); goto error; // Substatement should already have sent error + } } break; } case SQLCOM_ALTER_PROCEDURE: case SQLCOM_ALTER_FUNCTION: { - int result; + int sp_result; sp_head *sp; st_sp_chistics chistics; @@ -4671,7 +3815,7 @@ end_with_restore_list: if (! sp) { if (lex->spname->m_db.str) - result= SP_KEY_NOT_FOUND; + sp_result= SP_KEY_NOT_FOUND; else { my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); @@ -4696,7 +3840,7 @@ end_with_restore_list: { my_message(ER_BINLOG_UNSAFE_ROUTINE, ER(ER_BINLOG_UNSAFE_ROUTINE), MYF(0)); - result= SP_INTERNAL_ERROR; + sp_result= SP_INTERNAL_ERROR; } else { @@ -4706,21 +3850,17 @@ end_with_restore_list: follow the restrictions that log-bin-trust-function-creators=0 already puts on CREATE FUNCTION. */ + /* Conditionally writes to binlog */ if (lex->sql_command == SQLCOM_ALTER_PROCEDURE) - result= sp_update_procedure(thd, lex->spname, &lex->sp_chistics); + sp_result= sp_update_procedure(thd, lex->spname, + &lex->sp_chistics); else - result= sp_update_function(thd, lex->spname, &lex->sp_chistics); + sp_result= sp_update_function(thd, lex->spname, &lex->sp_chistics); } } - switch (result) + switch (sp_result) { case SP_OK: - if (mysql_bin_log.is_open()) - { - thd->clear_error(); - thd->binlog_query(THD::MYSQL_QUERY_TYPE, - thd->query, thd->query_length, FALSE, FALSE); - } send_ok(thd); break; case SP_KEY_NOT_FOUND: @@ -4737,13 +3877,13 @@ end_with_restore_list: case SQLCOM_DROP_PROCEDURE: case SQLCOM_DROP_FUNCTION: { - int result; + int sp_result; int type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ? TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); - result= sp_routine_exists_in_table(thd, type, lex->spname); + sp_result= sp_routine_exists_in_table(thd, type, lex->spname); mysql_reset_errors(thd, 0); - if (result == SP_OK) + if (sp_result == SP_OK) { char *db= lex->spname->m_db.str; char *name= lex->spname->m_name.str; @@ -4764,10 +3904,11 @@ end_with_restore_list: ER(ER_PROC_AUTO_REVOKE_FAIL)); } #endif + /* Conditionally writes to binlog */ if (lex->sql_command == SQLCOM_DROP_PROCEDURE) - result= sp_drop_procedure(thd, lex->spname); + sp_result= sp_drop_procedure(thd, lex->spname); else - result= sp_drop_function(thd, lex->spname); + sp_result= sp_drop_function(thd, lex->spname); } else { @@ -4780,6 +3921,8 @@ end_with_restore_list: { if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 0, 0)) goto error; + + /* Does NOT write to binlog */ if (!(res = mysql_drop_function(thd, &lex->spname->m_name))) { send_ok(thd); @@ -4789,23 +3932,16 @@ end_with_restore_list: } #endif if (lex->spname->m_db.str) - result= SP_KEY_NOT_FOUND; + sp_result= SP_KEY_NOT_FOUND; else { my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); goto error; } } - res= result; - switch (result) - { + res= sp_result; + switch (sp_result) { case SP_OK: - if (mysql_bin_log.is_open()) - { - thd->clear_error(); - thd->binlog_query(THD::MYSQL_QUERY_TYPE, - thd->query, thd->query_length, FALSE, FALSE); - } send_ok(thd); break; case SP_KEY_NOT_FOUND: @@ -4904,49 +4040,7 @@ end_with_restore_list: if (end_active_trans(thd)) goto error; - if (!(res= mysql_create_view(thd, thd->lex->create_view_mode)) && - mysql_bin_log.is_open()) - { - String buff; - const LEX_STRING command[3]= - {{ C_STRING_WITH_LEN("CREATE ") }, - { C_STRING_WITH_LEN("ALTER ") }, - { C_STRING_WITH_LEN("CREATE OR REPLACE ") }}; - thd->clear_error(); - - buff.append(command[thd->lex->create_view_mode].str, - command[thd->lex->create_view_mode].length); - view_store_options(thd, first_table, &buff); - buff.append(STRING_WITH_LEN("VIEW ")); - /* Test if user supplied a db (ie: we did not use thd->db) */ - if (first_table->db && first_table->db[0] && - (thd->db == NULL || strcmp(first_table->db, thd->db))) - { - append_identifier(thd, &buff, first_table->db, - first_table->db_length); - buff.append('.'); - } - append_identifier(thd, &buff, first_table->table_name, - first_table->table_name_length); - if (lex->view_list.elements) - { - List_iterator_fast<LEX_STRING> names(lex->view_list); - LEX_STRING *name; - int i; - - for (i= 0; name= names++; i++) - { - buff.append(i ? ", " : "("); - append_identifier(thd, &buff, name->str, name->length); - } - buff.append(')'); - } - buff.append(STRING_WITH_LEN(" AS ")); - buff.append(first_table->source.str, first_table->source.length); - - thd->binlog_query(THD::STMT_QUERY_TYPE, - buff.ptr(), buff.length(), FALSE, FALSE); - } + res= mysql_create_view(thd, first_table, thd->lex->create_view_mode); break; } case SQLCOM_DROP_VIEW: @@ -4954,13 +4048,8 @@ end_with_restore_list: if (check_table_access(thd, DROP_ACL, all_tables, 0) || end_active_trans(thd)) goto error; - if (!(res= mysql_drop_view(thd, first_table, thd->lex->drop_mode)) && - mysql_bin_log.is_open()) - { - thd->clear_error(); - thd->binlog_query(THD::STMT_QUERY_TYPE, - thd->query, thd->query_length, FALSE, FALSE); - } + /* Conditionally writes to binlog. */ + res= mysql_drop_view(thd, first_table, thd->lex->drop_mode); break; } case SQLCOM_CREATE_TRIGGER: @@ -4968,11 +4057,9 @@ end_with_restore_list: if (end_active_trans(thd)) goto error; + /* Conditionally writes to binlog. */ res= mysql_create_or_drop_trigger(thd, all_tables, 1); - /* We don't care about trigger body after this point */ - delete lex->sphead; - lex->sphead= 0; break; } case SQLCOM_DROP_TRIGGER: @@ -4980,6 +4067,7 @@ end_with_restore_list: if (end_active_trans(thd)) goto error; + /* Conditionally writes to binlog. */ res= mysql_create_or_drop_trigger(thd, all_tables, 0); break; } @@ -5180,6 +4268,58 @@ end_with_restore_list: #endif /* EMBEDDED_LIBRARY */ break; } + case SQLCOM_CREATE_SERVER: + { + int error; + LEX *lex= thd->lex; + DBUG_PRINT("info", ("case SQLCOM_CREATE_SERVER")); + if ((error= create_server(thd, &lex->server_options))) + { + DBUG_PRINT("info", ("problem creating server <%s>", + lex->server_options.server_name)); + my_error(error, MYF(0), lex->server_options.server_name); + break; + } + send_ok(thd, 1); + break; + } + case SQLCOM_ALTER_SERVER: + { + int error; + LEX *lex= thd->lex; + DBUG_PRINT("info", ("case SQLCOM_ALTER_SERVER")); + if ((error= alter_server(thd, &lex->server_options))) + { + DBUG_PRINT("info", ("problem altering server <%s>", + lex->server_options.server_name)); + my_error(error, MYF(0), lex->server_options.server_name); + break; + } + send_ok(thd, 1); + break; + } + case SQLCOM_DROP_SERVER: + { + int err_code; + LEX *lex= thd->lex; + DBUG_PRINT("info", ("case SQLCOM_DROP_SERVER")); + if ((err_code= drop_server(thd, &lex->server_options))) + { + if (! lex->drop_if_exists && err_code == ER_FOREIGN_SERVER_EXISTS) + { + DBUG_PRINT("info", ("problem dropping server %s", + lex->server_options.server_name)); + my_error(err_code, MYF(0), lex->server_options.server_name); + } + else + { + send_ok(thd, 0); + } + break; + } + send_ok(thd, 1); + break; + } default: #ifndef EMBEDDED_LIBRARY DBUG_ASSERT(0); /* Impossible */ @@ -5188,7 +4328,6 @@ end_with_restore_list: break; } -end: thd->proc_info="query end"; /* @@ -5566,7 +4705,9 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, { uint found=0; ulong found_access=0; +#ifndef EMBEDDED_LIBRARY TABLE_LIST *org_tables= tables; +#endif TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table(); Security_context *sctx= thd->security_ctx, *backup_ctx= thd->security_ctx; /* @@ -6072,12 +5213,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length) else #endif { - if (thd->net.report_error) - { - delete lex->sphead; - lex->sphead= NULL; - } - else + if (! thd->net.report_error) { /* Binlog logs a string starting from thd->query and having length @@ -6097,22 +5233,21 @@ void mysql_parse(THD *thd, char *inBuf, uint length) query_cache_end_of_result(thd); } } - lex->unit.cleanup(); } else { DBUG_ASSERT(thd->net.report_error); DBUG_PRINT("info",("Command aborted. Fatal_error: %d", thd->is_fatal_error)); + query_cache_abort(&thd->net); - lex->unit.cleanup(); - if (lex->sphead) - { - /* Clean up after failed stored procedure/function */ - delete lex->sphead; - lex->sphead= NULL; - } } + if (thd->lex->sphead) + { + delete thd->lex->sphead; + thd->lex->sphead= 0; + } + lex->unit.cleanup(); thd->proc_info="freeing items"; thd->end_statement(); thd->cleanup_after_query(); @@ -6201,7 +5336,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, */ if (default_value->type() == Item::FUNC_ITEM && !(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC && - type == FIELD_TYPE_TIMESTAMP)) + type == MYSQL_TYPE_TIMESTAMP)) { my_error(ER_INVALID_DEFAULT, MYF(0), field_name); DBUG_RETURN(1); @@ -6223,13 +5358,13 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, } } - if (on_update_value && type != FIELD_TYPE_TIMESTAMP) + if (on_update_value && type != MYSQL_TYPE_TIMESTAMP) { my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name); DBUG_RETURN(1); } - if (type == FIELD_TYPE_TIMESTAMP && length) + if (type == MYSQL_TYPE_TIMESTAMP && length) { /* Display widths are no longer supported for TIMSTAMP as of MySQL 4.1. In other words, for declarations such as TIMESTAMP(2), TIMESTAMP(4), @@ -6345,7 +5480,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, } if (table->is_derived_table() == FALSE && table->db.str && - check_db_name(table->db.str)) + check_db_name(&table->db)) { my_error(ER_WRONG_DB_NAME, MYF(0), table->db.str); DBUG_RETURN(0); @@ -6495,6 +5630,7 @@ bool st_select_lex::init_nested_join(THD *thd) join_list->push_front(ptr); ptr->embedding= embedding; ptr->join_list= join_list; + ptr->alias= (char*) "(nested_join)"; embedding= ptr; join_list= &nested_join->join_list; join_list->empty(); @@ -6579,6 +5715,7 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd) ptr->embedding= embedding; ptr->join_list= join_list; + ptr->alias= (char*) "(nest_last_join)"; embedded_list= &nested_join->join_list; embedded_list->empty(); @@ -6595,11 +5732,8 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd) If this is a JOIN ... USING, move the list of joined fields to the table reference that describes the join. */ - if (table->join_using_fields) - { - ptr->join_using_fields= table->join_using_fields; - table->join_using_fields= NULL; - } + if (prev_join_using) + ptr->join_using_fields= prev_join_using; } } join_list->push_front(ptr); @@ -6735,18 +5869,18 @@ void st_select_lex::set_lock_for_tables(thr_lock_type lock_type) 0 on success */ -bool st_select_lex_unit::add_fake_select_lex(THD *thd) +bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg) { SELECT_LEX *first_sl= first_select(); DBUG_ENTER("add_fake_select_lex"); DBUG_ASSERT(!fake_select_lex); - if (!(fake_select_lex= new (thd->mem_root) SELECT_LEX())) + if (!(fake_select_lex= new (thd_arg->mem_root) SELECT_LEX())) DBUG_RETURN(1); fake_select_lex->include_standalone(this, (SELECT_LEX_NODE**)&fake_select_lex); fake_select_lex->select_number= INT_MAX; - fake_select_lex->parent_lex= thd->lex; /* Used in init_query. */ + fake_select_lex->parent_lex= thd_arg->lex; /* Used in init_query. */ fake_select_lex->make_empty_select(); fake_select_lex->linkage= GLOBAL_OPTIONS_TYPE; fake_select_lex->select_limit= 0; @@ -6766,9 +5900,9 @@ bool st_select_lex_unit::add_fake_select_lex(THD *thd) */ global_parameters= fake_select_lex; fake_select_lex->no_table_names_allowed= 1; - thd->lex->current_select= fake_select_lex; + thd_arg->lex->current_select= fake_select_lex; } - thd->lex->pop_context(); + thd_arg->lex->pop_context(); DBUG_RETURN(0); } @@ -6855,6 +5989,7 @@ void add_join_on(TABLE_LIST *b, Item *expr) a Left join argument b Right join argument using_fields Field names from USING clause + lex The current st_select_lex IMPLEMENTATION This function marks that table b should be joined with a either via @@ -6883,10 +6018,11 @@ void add_join_on(TABLE_LIST *b, Item *expr) None */ -void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields) +void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields, + SELECT_LEX *lex) { b->natural_join= a; - b->join_using_fields= using_fields; + lex->prev_join_using= using_fields; } @@ -6962,7 +6098,10 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, than it would help them) */ tmp_write_to_binlog= 0; - mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE); + if( mysql_bin_log.is_open() ) + { + mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE); + } #ifdef HAVE_REPLICATION pthread_mutex_lock(&LOCK_active_mi); rotate_relay_log(active_mi); @@ -7071,7 +6210,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, } #endif if (options & REFRESH_USER_RESOURCES) - reset_mqh((LEX_USER *) NULL); + reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */ *write_to_binlog= tmp_write_to_binlog; return result; } @@ -7291,6 +6430,7 @@ bool mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys) bzero((char*) &create_info,sizeof(create_info)); create_info.db_type= 0; create_info.default_table_charset= thd->variables.collation_database; + create_info.row_type= ROW_TYPE_NOT_USED; DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->table_name, &create_info, table_list, fields, keys, 0, (ORDER*)0, @@ -7307,6 +6447,7 @@ bool mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info) bzero((char*) &create_info,sizeof(create_info)); create_info.db_type= 0; create_info.default_table_charset= thd->variables.collation_database; + create_info.row_type= ROW_TYPE_NOT_USED; alter_info->clear(); alter_info->flags= ALTER_DROP_INDEX; DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->table_name, @@ -7629,7 +6770,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, #ifdef NOT_NECESSARY_TO_CHECK_CREATE_TABLE_EXIST_WHEN_PREPARING_STATEMENT /* This code throws an ill error for CREATE TABLE t1 SELECT * FROM t1 */ /* - Only do the check for PS, becasue we on execute we have to check that + Only do the check for PS, because we on execute we have to check that against the opened tables to ensure we don't use a table that is part of the view (which can only be done after the table has been opened). */ diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 8df527fd25b..dbac53ed5f6 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -886,7 +885,6 @@ int check_signed_flag(partition_info *part_info) bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, bool is_sub_part, bool is_field_to_be_setup) { - MEM_ROOT new_mem_root; partition_info *part_info= table->part_info; uint dir_length, home_dir_length; bool result= TRUE; @@ -2002,9 +2000,8 @@ char *generate_partition_syntax(partition_info *part_info, bool use_sql_alloc, bool show_partition_options) { - uint i,j, tot_no_parts, no_subparts, no_parts; + uint i,j, tot_no_parts, no_subparts; partition_element *part_elem; - partition_element *save_part_elem= NULL; ulonglong buffer_length; char path[FN_REFLEN]; int err= 0; @@ -2302,10 +2299,13 @@ static uint32 get_part_id_hash(uint no_parts, Item *part_expr, longlong *func_value) { + longlong int_hash_id; DBUG_ENTER("get_part_id_hash"); + *func_value= part_val_int(part_expr); - longlong int_hash_id= *func_value % no_parts; - DBUG_RETURN(int_hash_id < 0 ? -int_hash_id : int_hash_id); + int_hash_id= *func_value % no_parts; + + DBUG_RETURN(int_hash_id < 0 ? (uint32) -int_hash_id : (uint32) int_hash_id); } @@ -2358,7 +2358,7 @@ static uint32 get_part_id_key(Field **field_array, { DBUG_ENTER("get_part_id_key"); *func_value= calculate_key_value(field_array); - DBUG_RETURN(*func_value % no_parts); + DBUG_RETURN((uint32) (*func_value % no_parts)); } @@ -3936,7 +3936,7 @@ static int fast_end_partition(THD *thd, ulonglong copied, (ulong) (copied + deleted), (ulong) deleted, (ulong) 0); - send_ok(thd,copied+deleted,0L,tmp_name); + send_ok(thd, (ha_rows) (copied+deleted),0L,tmp_name); DBUG_RETURN(FALSE); } table->file->print_error(error, MYF(0)); @@ -4024,7 +4024,6 @@ static bool check_native_partitioned(HA_CREATE_INFO *create_info,bool *ret_val, handlerton *engine_type= create_info->db_type; handlerton *old_engine_type= engine_type; uint i= 0; - handler *file; uint no_parts= part_info->partitions.elements; DBUG_ENTER("check_native_partitioned"); @@ -4480,7 +4479,7 @@ that are reorganised. { if (!alt_part_info->use_default_partitions) { - DBUG_PRINT("info", ("part_info= %x", tab_part_info)); + DBUG_PRINT("info", ("part_info: 0x%lx", (long) tab_part_info)); tab_part_info->use_default_partitions= FALSE; } tab_part_info->use_default_no_partitions= FALSE; @@ -4738,6 +4737,7 @@ state of p1. } alt_part_info->part_type= tab_part_info->part_type; alt_part_info->subpart_type= tab_part_info->subpart_type; + alt_part_info->no_subparts= tab_part_info->no_subparts; DBUG_ASSERT(!alt_part_info->use_default_partitions); if (alt_part_info->set_up_defaults_for_partitioning(table->file, ULL(0), @@ -5368,7 +5368,6 @@ static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, List_iterator<partition_element> temp_it(part_info->temp_partitions); uint no_temp_partitions= part_info->temp_partitions.elements; uint no_elements= part_info->partitions.elements; - uint i= 0; DBUG_ENTER("write_log_dropped_partitions"); ddl_log_entry.action_type= DDL_LOG_DELETE_ACTION; @@ -5476,7 +5475,6 @@ static void set_part_info_exec_log_entry(partition_info *part_info, static bool write_log_drop_shadow_frm(ALTER_PARTITION_PARAM_TYPE *lpt) { - DDL_LOG_ENTRY ddl_log_entry; partition_info *part_info= lpt->part_info; DDL_LOG_MEMORY_ENTRY *log_entry; DDL_LOG_MEMORY_ENTRY *exec_log_entry= NULL; @@ -5521,7 +5519,6 @@ error: static bool write_log_rename_frm(ALTER_PARTITION_PARAM_TYPE *lpt) { - DDL_LOG_ENTRY ddl_log_entry; partition_info *part_info= lpt->part_info; DDL_LOG_MEMORY_ENTRY *log_entry; DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry; @@ -5574,7 +5571,6 @@ error: static bool write_log_drop_partition(ALTER_PARTITION_PARAM_TYPE *lpt) { - DDL_LOG_ENTRY ddl_log_entry; partition_info *part_info= lpt->part_info; DDL_LOG_MEMORY_ENTRY *log_entry; DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry; @@ -5688,7 +5684,6 @@ error: static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt) { - DDL_LOG_ENTRY ddl_log_entry; partition_info *part_info= lpt->part_info; DDL_LOG_MEMORY_ENTRY *log_entry; DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry; @@ -5745,8 +5740,6 @@ static void write_log_completed(ALTER_PARTITION_PARAM_TYPE *lpt, bool dont_crash) { partition_info *part_info= lpt->part_info; - uint count_loop= 0; - bool not_success; DDL_LOG_MEMORY_ENTRY *log_entry= part_info->exec_log_entry; DBUG_ENTER("write_log_completed"); @@ -6020,8 +6013,6 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, uint fast_alter_partition) { /* Set-up struct used to write frm files */ - ulonglong copied= 0; - ulonglong deleted= 0; partition_info *part_info= table->part_info; ALTER_PARTITION_PARAM_TYPE lpt_obj; ALTER_PARTITION_PARAM_TYPE *lpt= &lpt_obj; @@ -6740,7 +6731,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info, } } else - DBUG_ASSERT(0); + assert(0); /* Find minimum: Do special handling if the interval has left bound in form @@ -7055,7 +7046,6 @@ static uint32 get_next_partition_via_walking(PARTITION_ITERATOR *part_iter) static uint32 get_next_subpartition_via_walking(PARTITION_ITERATOR *part_iter) { - uint32 part_id; Field *field= part_iter->part_info->subpart_field_array[0]; if (part_iter->field_vals.cur == part_iter->field_vals.end) { diff --git a/sql/sql_partition.h b/sql/sql_partition.h index 4372c26a851..7ed43527688 100644 --- a/sql/sql_partition.h +++ b/sql/sql_partition.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 34fb447792e..e3e24c1f375 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -23,29 +22,46 @@ extern struct st_mysql_plugin *mysqld_builtins[]; char *opt_plugin_dir_ptr; char opt_plugin_dir[FN_REFLEN]; +/* + When you ad a new plugin type, add both a string and make sure that the + init and deinit array are correctly updated. +*/ const LEX_STRING plugin_type_names[MYSQL_MAX_PLUGIN_TYPE_NUM]= { { C_STRING_WITH_LEN("UDF") }, { C_STRING_WITH_LEN("STORAGE ENGINE") }, - { C_STRING_WITH_LEN("FTPARSER") } + { C_STRING_WITH_LEN("FTPARSER") }, + { C_STRING_WITH_LEN("DAEMON") }, + { C_STRING_WITH_LEN("INFORMATION SCHEMA") } }; +extern int initialize_schema_table(st_plugin_int *plugin); +extern int finalize_schema_table(st_plugin_int *plugin); + +/* + The number of elements in both plugin_type_initialize and + plugin_type_deinitialize should equal to the number of plugins + defined. +*/ plugin_type_init plugin_type_initialize[MYSQL_MAX_PLUGIN_TYPE_NUM]= { - 0,ha_initialize_handlerton,0 + 0,ha_initialize_handlerton,0,0,initialize_schema_table }; plugin_type_init plugin_type_deinitialize[MYSQL_MAX_PLUGIN_TYPE_NUM]= { - 0,ha_finalize_handlerton,0 + 0,ha_finalize_handlerton,0,0,finalize_schema_table }; +#ifdef HAVE_DLOPEN static const char *plugin_interface_version_sym= "_mysql_plugin_interface_version_"; static const char *sizeof_st_plugin_sym= "_mysql_sizeof_struct_st_plugin_"; static const char *plugin_declarations_sym= "_mysql_plugin_declarations_"; static int min_plugin_interface_version= MYSQL_PLUGIN_INTERFACE_VERSION & ~0xFF; +#endif + /* Note that 'int version' must be the first field of every plugin sub-structure (plugin->info). */ @@ -53,13 +69,17 @@ static int min_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]= { 0x0000, MYSQL_HANDLERTON_INTERFACE_VERSION, - MYSQL_FTPARSER_INTERFACE_VERSION + MYSQL_FTPARSER_INTERFACE_VERSION, + MYSQL_DAEMON_INTERFACE_VERSION, + MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION }; static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]= { 0x0000, /* UDF: not implemented */ MYSQL_HANDLERTON_INTERFACE_VERSION, - MYSQL_FTPARSER_INTERFACE_VERSION + MYSQL_FTPARSER_INTERFACE_VERSION, + MYSQL_DAEMON_INTERFACE_VERSION, + MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION }; static DYNAMIC_ARRAY plugin_dl_array; @@ -74,6 +94,8 @@ static int plugin_array_version=0; my_bool plugin_register_builtin(struct st_mysql_plugin *plugin); void plugin_load(void); +#ifdef HAVE_DLOPEN + static struct st_plugin_dl *plugin_dl_find(const LEX_STRING *dl) { uint i; @@ -111,6 +133,8 @@ static st_plugin_dl *plugin_dl_insert_or_reuse(struct st_plugin_dl *plugin_dl) DBUG_RETURN(dynamic_element(&plugin_dl_array, plugin_dl_array.elements - 1, struct st_plugin_dl *)); } +#endif /* HAVE_DLOPEN */ + static inline void free_plugin_mem(struct st_plugin_dl *p) { @@ -502,13 +526,13 @@ void plugin_deinitialize(struct st_plugin_int *plugin) if ((*plugin_type_deinitialize[plugin->plugin->type])(plugin)) { sql_print_error("Plugin '%s' of type %s failed deinitialization", - plugin->name.str, plugin_type_names[plugin->plugin->type]); + plugin->name.str, plugin_type_names[plugin->plugin->type].str); } } else if (plugin->plugin->deinit) { DBUG_PRINT("info", ("Deinitializing plugin: '%s'", plugin->name.str)); - if (plugin->plugin->deinit(NULL)) + if (plugin->plugin->deinit(plugin)) { DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.", plugin->name.str)); @@ -528,6 +552,8 @@ static void plugin_del(struct st_plugin_int *plugin) DBUG_VOID_RETURN; } +#ifdef NOT_USED + static void plugin_del(const LEX_STRING *name) { struct st_plugin_int *plugin; @@ -537,6 +563,8 @@ static void plugin_del(const LEX_STRING *name) DBUG_VOID_RETURN; } +#endif + void plugin_unlock(struct st_plugin_int *plugin) { DBUG_ENTER("plugin_unlock"); @@ -562,13 +590,13 @@ static int plugin_initialize(struct st_plugin_int *plugin) if ((*plugin_type_initialize[plugin->plugin->type])(plugin)) { sql_print_error("Plugin '%s' registration as a %s failed.", - plugin->name.str, plugin_type_names[plugin->plugin->type]); + plugin->name.str, plugin_type_names[plugin->plugin->type].str); goto err; } } else if (plugin->plugin->init) { - if (plugin->plugin->init(NULL)) + if (plugin->plugin->init(plugin)) { sql_print_error("Plugin '%s' init function returned error.", plugin->name.str); @@ -727,7 +755,7 @@ void plugin_load(void) TABLE_LIST tables; TABLE *table; READ_RECORD read_record_info; - int error, i; + int error; MEM_ROOT mem; THD *new_thd; DBUG_ENTER("plugin_load"); @@ -944,29 +972,28 @@ my_bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func, state_mask= ~state_mask; // do it only once rw_rdlock(&THR_LOCK_plugin); + total= type == MYSQL_ANY_PLUGIN ? plugin_array.elements + : plugin_hash[type].records; + /* + Do the alloca out here in case we do have a working alloca: + leaving the nested stack frame invalidates alloca allocation. + */ + plugins=(struct st_plugin_int **)my_alloca(total*sizeof(*plugins)); if (type == MYSQL_ANY_PLUGIN) { - total=plugin_array.elements; - plugins=(struct st_plugin_int **)my_alloca(total*sizeof(*plugins)); for (idx= 0; idx < total; idx++) { plugin= dynamic_element(&plugin_array, idx, struct st_plugin_int *); - if (plugin->state & state_mask) - continue; - plugins[idx]= plugin; + plugins[idx]= !(plugin->state & state_mask) ? plugin : NULL; } } else { - HASH *hash= &plugin_hash[type]; - total=hash->records; - plugins=(struct st_plugin_int **)my_alloca(total*sizeof(*plugins)); + HASH *hash= plugin_hash + type; for (idx= 0; idx < total; idx++) { plugin= (struct st_plugin_int *) hash_element(hash, idx); - if (plugin->state & state_mask) - continue; - plugins[idx]= plugin; + plugins[idx]= !(plugin->state & state_mask) ? plugin : NULL; } } rw_unlock(&THR_LOCK_plugin); @@ -977,7 +1004,7 @@ my_bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func, { rw_rdlock(&THR_LOCK_plugin); for (uint i=idx; i < total; i++) - if (plugins[i]->state & state_mask) + if (plugins[i] && plugins[i]->state & state_mask) plugins[i]=0; rw_unlock(&THR_LOCK_plugin); } diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index fa3440f7f68..d86d9332a92 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 63a129918d0..ce6072b2a63 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -100,9 +99,12 @@ public: #endif }; -/****************************************************************************** - Prepared_statement: a statement that can contain placeholders -******************************************************************************/ +/****************************************************************************/ + +/** + @class Prepared_statement + @brief Prepared_statement: a statement that can contain placeholders +*/ class Prepared_statement: public Statement { @@ -142,6 +144,16 @@ public: bool execute(String *expanded_query, bool open_cursor); /* Destroy this statement */ bool deallocate(); +private: + /** + Store the parsed tree of a prepared statement here. + */ + LEX main_lex; + /** + The memory root to allocate parsed tree elements (instances of Item, + SELECT_LEX and other classes). + */ + MEM_ROOT main_mem_root; }; @@ -977,19 +989,19 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, /* Insert @'escaped-varname' instead of parameter in the query */ if (entry) { - char *begin, *ptr; + char *start, *ptr; buf.length(0); if (buf.reserve(entry->name.length*2+3)) DBUG_RETURN(1); - begin= ptr= buf.c_ptr_quick(); + start= ptr= buf.c_ptr_quick(); *ptr++= '@'; *ptr++= '\''; ptr+= escape_string_for_mysql(&my_charset_utf8_general_ci, ptr, 0, entry->name.str, entry->name.length); *ptr++= '\''; - buf.length(ptr - begin); + buf.length(ptr - start); val= &buf; } else @@ -1027,7 +1039,6 @@ static bool mysql_test_insert(Prepared_statement *stmt, enum_duplicates duplic) { THD *thd= stmt->thd; - LEX *lex= stmt->lex; List_iterator_fast<List_item> its(values_list); List_item *values; DBUG_ENTER("mysql_test_insert"); @@ -1254,7 +1265,6 @@ static int mysql_test_select(Prepared_statement *stmt, lex->select_lex.context.resolve_in_select_list= TRUE; -#ifndef NO_EMBEDDED_ACCESS_CHECKS ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL; if (tables) { @@ -1263,7 +1273,6 @@ static int mysql_test_select(Prepared_statement *stmt, } else if (check_access(thd, privilege, any_db,0,0,0,0)) goto error; -#endif if (!lex->result && !(lex->result= new (stmt->mem_root) select_send)) { @@ -1575,21 +1584,16 @@ error: static bool mysql_insert_select_prepare_tester(THD *thd) { - TABLE_LIST *first; - bool res; SELECT_LEX *first_select= &thd->lex->select_lex; + TABLE_LIST *second_table= ((TABLE_LIST*)first_select->table_list.first)-> + next_local; + /* Skip first table, which is the table we are inserting in */ - first_select->table_list.first= (byte*)(first= - ((TABLE_LIST*)first_select-> - table_list.first)->next_local); - res= mysql_insert_select_prepare(thd); - /* - insert/replace from SELECT give its SELECT_LEX for SELECT, - and item_list belong to SELECT - */ - thd->lex->select_lex.context.resolve_in_select_list= TRUE; - thd->lex->select_lex.context.table_list= first; - return res; + first_select->table_list.first= (byte *) second_table; + thd->lex->select_lex.context.table_list= + thd->lex->select_lex.context.first_name_resolution_table= second_table; + + return mysql_insert_select_prepare(thd); } @@ -1665,7 +1669,7 @@ static bool check_prepared_statement(Prepared_statement *stmt, enum enum_sql_command sql_command= lex->sql_command; int res= 0; DBUG_ENTER("check_prepared_statement"); - DBUG_PRINT("enter",("command: %d, param_count: %ld", + DBUG_PRINT("enter",("command: %d, param_count: %u", sql_command, stmt->param_count)); lex->first_lists_tables_same(); @@ -1680,7 +1684,7 @@ static bool check_prepared_statement(Prepared_statement *stmt, case SQLCOM_INSERT: res= mysql_test_insert(stmt, tables, lex->field_list, lex->many_values, - select_lex->item_list, lex->value_list, + lex->update_list, lex->value_list, lex->duplicates); break; @@ -1916,9 +1920,12 @@ void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length) thd->stmt_map.erase(stmt); } else - general_log_print(thd, COM_STMT_PREPARE, "[%lu] %.*b", stmt->id, + { + const char *format= "[%lu] %.*b"; + general_log_print(thd, COM_STMT_PREPARE, format, stmt->id, stmt->query_length, stmt->query); + } /* check_prepared_statemnt sends the metadata packet in case of success */ DBUG_VOID_RETURN; } @@ -2055,6 +2062,7 @@ void mysql_sql_stmt_prepare(THD *thd) uint query_len; DBUG_ENTER("mysql_sql_stmt_prepare"); DBUG_ASSERT(thd->protocol == &thd->protocol_simple); + LINT_INIT(query_len); if ((stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name))) { @@ -2078,6 +2086,7 @@ void mysql_sql_stmt_prepare(THD *thd) delete stmt; DBUG_VOID_RETURN; } + if (thd->stmt_map.insert(thd, stmt)) { /* The statement is deleted and an error is set if insert fails */ @@ -2243,11 +2252,11 @@ void mysql_stmt_execute(THD *thd, char *packet_arg, uint packet_length) { uchar *packet= (uchar*)packet_arg; // GCC 4.0.1 workaround ulong stmt_id= uint4korr(packet); - ulong flags= (ulong) ((uchar) packet[4]); + ulong flags= (ulong) packet[4]; /* Query text for binary, general or slow log, if any of them is open */ String expanded_query; #ifndef EMBEDDED_LIBRARY - uchar *packet_end= (uchar *) packet + packet_length - 1; + uchar *packet_end= packet + packet_length - 1; #endif Prepared_statement *stmt; bool error; @@ -2262,7 +2271,7 @@ void mysql_stmt_execute(THD *thd, char *packet_arg, uint packet_length) DBUG_VOID_RETURN; DBUG_PRINT("exec_query", ("%s", stmt->query)); - DBUG_PRINT("info",("stmt: %p", stmt)); + DBUG_PRINT("info",("stmt: 0x%lx", (long) stmt)); sp_cache_flush_obsolete(&thd->sp_proc_cache); sp_cache_flush_obsolete(&thd->sp_func_cache); @@ -2270,9 +2279,9 @@ void mysql_stmt_execute(THD *thd, char *packet_arg, uint packet_length) #ifndef EMBEDDED_LIBRARY if (stmt->param_count) { - uchar *null_array= (uchar *) packet; - if (setup_conversion_functions(stmt, (uchar **) &packet, packet_end) || - stmt->set_params(stmt, null_array, (uchar *) packet, packet_end, + uchar *null_array= packet; + if (setup_conversion_functions(stmt, &packet, packet_end) || + stmt->set_params(stmt, null_array, packet, packet_end, &expanded_query)) goto set_params_data_err; } @@ -2287,14 +2296,24 @@ void mysql_stmt_execute(THD *thd, char *packet_arg, uint packet_length) #endif if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),QUERY_PRIOR); + + /* + If the free_list is not empty, we'll wrongly free some externally + allocated items when cleaning up after validation of the prepared + statement. + */ + DBUG_ASSERT(thd->free_list == NULL); + error= stmt->execute(&expanded_query, test(flags & (ulong) CURSOR_TYPE_READ_ONLY)); if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(), WAIT_PRIOR); if (error == 0) - general_log_print(thd, COM_STMT_EXECUTE, "[%lu] %.*b", stmt->id, + { + const char *format= "[%lu] %.*b"; + general_log_print(thd, COM_STMT_EXECUTE, format, stmt->id, thd->query_length, thd->query); - + } DBUG_VOID_RETURN; set_params_data_err: @@ -2347,7 +2366,14 @@ void mysql_sql_stmt_execute(THD *thd) DBUG_VOID_RETURN; } - DBUG_PRINT("info",("stmt: %p", stmt)); + DBUG_PRINT("info",("stmt: 0x%lx", (long) stmt)); + + /* + If the free_list is not empty, we'll wrongly free some externally + allocated items when cleaning up after validation of the prepared + statement. + */ + DBUG_ASSERT(thd->free_list == NULL); if (stmt->set_params_from_vars(stmt, lex->prepared_stmt_params, &expanded_query)) @@ -2549,7 +2575,9 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length) uint param_number; Prepared_statement *stmt; Item_param *param; +#ifndef EMBEDDED_LIBRARY char *packet_end= packet + packet_length - 1; +#endif DBUG_ENTER("mysql_stmt_get_longdata"); statistic_increment(thd->status_var.com_stmt_send_long_data, &LOCK_status); @@ -2603,8 +2631,8 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length) Select_fetch_protocol_prep ****************************************************************************/ -Select_fetch_protocol_prep::Select_fetch_protocol_prep(THD *thd) - :protocol(thd) +Select_fetch_protocol_prep::Select_fetch_protocol_prep(THD *thd_arg) + :protocol(thd_arg) {} bool Select_fetch_protocol_prep::send_fields(List<Item> &list, uint flags) @@ -2653,17 +2681,18 @@ Select_fetch_protocol_prep::send_data(List<Item> &fields) ****************************************************************************/ Prepared_statement::Prepared_statement(THD *thd_arg, Protocol *protocol_arg) - :Statement(INITIALIZED, ++thd_arg->statement_id_counter, - thd_arg->variables.query_alloc_block_size, - thd_arg->variables.query_prealloc_size), + :Statement(&main_lex, &main_mem_root, + INITIALIZED, ++thd_arg->statement_id_counter), thd(thd_arg), result(thd_arg), protocol(protocol_arg), param_array(0), param_count(0), last_errno(0), - flags((uint) IS_IN_USE) + flags((uint) IS_IN_USE) { + init_alloc_root(&main_mem_root, thd_arg->variables.query_alloc_block_size, + thd_arg->variables.query_prealloc_size); *last_error= '\0'; } @@ -2704,7 +2733,8 @@ void Prepared_statement::setup_set_params() Prepared_statement::~Prepared_statement() { DBUG_ENTER("Prepared_statement::~Prepared_statement"); - DBUG_PRINT("enter",("stmt: %p cursor: %p", this, cursor)); + DBUG_PRINT("enter",("stmt: 0x%lx cursor: 0x%lx", + (long) this, (long) cursor)); delete cursor; /* We have to call free on the items even if cleanup is called as some items, @@ -2712,6 +2742,7 @@ Prepared_statement::~Prepared_statement() */ free_items(); delete lex->result; + free_root(&main_mem_root, MYF(0)); DBUG_VOID_RETURN; } @@ -2725,8 +2756,9 @@ Query_arena::Type Prepared_statement::type() const void Prepared_statement::cleanup_stmt() { DBUG_ENTER("Prepared_statement::cleanup_stmt"); - DBUG_PRINT("enter",("stmt: %p", this)); + DBUG_PRINT("enter",("stmt: 0x%lx", (long) this)); + DBUG_ASSERT(lex->sphead == 0); /* The order is important */ lex->unit.cleanup(); cleanup_items(free_list); @@ -2811,6 +2843,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) error= MYSQLparse((void *)thd) || thd->is_fatal_error || thd->net.report_error || init_param_array(this); + lex->safe_to_cache_query= FALSE; /* While doing context analysis of the query (in check_prepared_statement) @@ -2827,21 +2860,28 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) external changes when cleaning up after validation. */ DBUG_ASSERT(thd->change_list.is_empty()); - /* - If the free_list is not empty, we'll wrongly free some externally - allocated items when cleaning up after validation of the prepared - statement. + + /* + The only case where we should have items in the thd->free_list is + after stmt->set_params_from_vars(), which may in some cases create + Item_null objects. */ - DBUG_ASSERT(thd->free_list == NULL); if (error == 0) error= check_prepared_statement(this, name.str != 0); - if (error && lex->sphead) + /* + Currently CREATE PROCEDURE/TRIGGER/EVENT are prohibited in prepared + statements: ensure we have no memory leak here if by someone tries + to PREPARE stmt FROM "CREATE PROCEDURE ..." + */ + DBUG_ASSERT(lex->sphead == NULL || error != 0); + if (lex->sphead) { delete lex->sphead; lex->sphead= NULL; } + lex_end(lex); cleanup_stmt(); thd->restore_backup_statement(this, &stmt_backup); @@ -2886,7 +2926,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) { Statement stmt_backup; Query_arena *old_stmt_arena; - Item *old_free_list; bool error= TRUE; statistic_increment(thd->status_var.com_stmt_execute, &LOCK_status); @@ -2913,10 +2952,9 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) in INSERT ... SELECT and similar commands. */ - if (open_cursor && lex->result && !lex->result->simple_select()) + if (open_cursor && lex->result && lex->result->check_simple_select()) { DBUG_PRINT("info",("Cursor asked for not SELECT stmt")); - my_error(ER_SP_BAD_CURSOR_QUERY, MYF(0)); return TRUE; } @@ -2930,7 +2968,13 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) allocated items when cleaning up after execution of this statement. */ DBUG_ASSERT(thd->change_list.is_empty()); - DBUG_ASSERT(thd->free_list == NULL); + + /* + The only case where we should have items in the thd->free_list is + after stmt->set_params_from_vars(), which may in some cases create + Item_null objects. + */ + thd->set_n_backup_statement(this, &stmt_backup); if (expanded_query->length() && alloc_query(thd, (char*) expanded_query->ptr(), diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index bf2e2d506cd..f34ec83b29c 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -36,6 +35,9 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) { bool error= 1; TABLE_LIST *ren_table= 0; + int to_table; + char *rename_log_table[2]= {NULL, NULL}; + int disable_logs= 0; DBUG_ENTER("mysql_rename_tables"); /* @@ -52,6 +54,96 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) if (wait_if_global_read_lock(thd,0,1)) DBUG_RETURN(1); + + if (logger.is_log_table_enabled(QUERY_LOG_GENERAL) || + logger.is_log_table_enabled(QUERY_LOG_SLOW)) + { + + /* + Rules for rename of a log table: + + IF 1. Log tables are enabled + AND 2. Rename operates on the log table and nothing is being + renamed to the log table. + DO 3. Throw an error message. + ELSE 4. Perform rename. + */ + + for (to_table= 0, ren_table= table_list; ren_table; + to_table= 1 - to_table, ren_table= ren_table->next_local) + { + int log_table_rename= 0; + + if ((log_table_rename= + check_if_log_table(ren_table->db_length, ren_table->db, + ren_table->table_name_length, + ren_table->table_name, 1))) + { + /* + Log table encoutered we will need to disable and lock logs + for duration of rename. + */ + disable_logs= TRUE; + + /* + as we use log_table_rename as an array index, we need it to start + with 0, while QUERY_LOG_SLOW == 1 and QUERY_LOG_GENERAL == 2. + So, we shift the value to start with 0; + */ + log_table_rename--; + if (rename_log_table[log_table_rename]) + { + if (to_table) + rename_log_table[log_table_rename]= NULL; + else + { + /* + Two renames of "log_table TO" w/o rename "TO log_table" in + between. + */ + my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), ren_table->table_name, + ren_table->table_name); + DBUG_RETURN(1); + } + } + else + { + if (to_table) + { + /* + Attempt to rename a table TO log_table w/o renaming + log_table TO some table. + */ + my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), ren_table->table_name, + ren_table->table_name); + DBUG_RETURN(1); + } + else + { + /* save the name of the log table to report an error */ + rename_log_table[log_table_rename]= ren_table->table_name; + } + } + } + } + if (rename_log_table[0] || rename_log_table[1]) + { + if (rename_log_table[0]) + my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), rename_log_table[0], + rename_log_table[0]); + else + my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), rename_log_table[1], + rename_log_table[1]); + DBUG_RETURN(1); + } + + if (disable_logs) + { + logger.lock(); + logger.tmp_close_log_tables(thd); + } + } + VOID(pthread_mutex_lock(&LOCK_open)); if (lock_table_names(thd, table_list)) goto err; @@ -95,6 +187,13 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) err: pthread_mutex_unlock(&LOCK_open); + /* enable logging back if needed */ + if (disable_logs) + { + if (logger.reopen_log_tables()) + error= TRUE; + logger.unlock(); + } start_waiting_global_read_lock(thd); DBUG_RETURN(error); } @@ -253,7 +352,7 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name, static TABLE_LIST * rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) { - TABLE_LIST *ren_table,*new_table, *tmp_table; + TABLE_LIST *ren_table, *new_table; DBUG_ENTER("rename_tables"); diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 0fff99fbc8f..debc9a7b572 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB & Sasha +/* Copyright (C) 2000-2006 MySQL AB & Sasha 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -24,7 +23,9 @@ int max_binlog_dump_events = 0; // unlimited my_bool opt_sporadic_binlog_dump_fail = 0; +#ifndef DBUG_OFF static int binlog_dump_count = 0; +#endif /* fake_rotate_event() builds a fake (=which does not exist physically in any @@ -93,8 +94,8 @@ static int send_file(THD *thd) The client might be slow loading the data, give him wait_timeout to do the job */ - old_timeout = thd->net.read_timeout; - thd->net.read_timeout = thd->variables.net_wait_timeout; + old_timeout= net->read_timeout; + net_set_read_timeout(net, thd->variables.net_wait_timeout); /* We need net_flush here because the client will not know it needs to send @@ -138,7 +139,7 @@ static int send_file(THD *thd) error = 0; err: - thd->net.read_timeout = old_timeout; + net_set_read_timeout(net, old_timeout); if (fd >= 0) (void) my_close(fd, MYF(0)); if (errmsg) @@ -239,6 +240,7 @@ bool purge_error_message(THD* thd, int res) case LOG_INFO_MEM: errmsg= ER_OUT_OF_RESOURCES; break; case LOG_INFO_FATAL: errmsg= ER_BINLOG_PURGE_FATAL_ERR; break; case LOG_INFO_IN_USE: errmsg= ER_LOG_IN_USE; break; + case LOG_INFO_EMFILE: errmsg= ER_BINLOG_PURGE_EMFILE; break; default: errmsg= ER_LOG_PURGE_UNKNOWN_ERR; break; } @@ -431,6 +433,12 @@ impossible position"; goto err; } packet->set("\0", 1, &my_charset_bin); + /* + Adding MAX_LOG_EVENT_HEADER_LEN, since a binlog event can become + this larger than the corresponding packet (query) sent + from client to master. + */ + thd->variables.max_allowed_packet+= MAX_LOG_EVENT_HEADER; /* We can set log_lock now, it does not move (it's a member of @@ -878,12 +886,14 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report) int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report ) { + DBUG_ENTER("stop_slave"); + int slave_errno; if (!thd) thd = current_thd; if (check_access(thd, SUPER_ACL, any_db,0,0,0,0)) - return 1; + DBUG_RETURN(1); thd->proc_info = "Killing slave"; int thread_mask; lock_slave_threads(mi); @@ -917,12 +927,12 @@ int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report ) { if (net_report) my_message(slave_errno, ER(slave_errno), MYF(0)); - return 1; + DBUG_RETURN(1); } else if (net_report) send_ok(thd); - return 0; + DBUG_RETURN(0); } @@ -1105,7 +1115,7 @@ bool change_master(THD* thd, MASTER_INFO* mi) { mi->master_log_pos= lex_mi->pos; } - DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos)); + DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos)); if (lex_mi->host) strmake(mi->host, lex_mi->host, sizeof(mi->host)-1); @@ -1222,7 +1232,7 @@ bool change_master(THD* thd, MASTER_INFO* mi) } } mi->rli.group_master_log_pos = mi->master_log_pos; - DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos)); + DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos)); /* Coordinates in rli were spoilt by the 'if (need_relay_log_purge)' block, diff --git a/sql/sql_repl.h b/sql/sql_repl.h index 789de64da85..b106391245d 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB & Sasha +/* Copyright (C) 2000-2006 MySQL AB & Sasha 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 7908bb8c95f..c901655f2d3 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000-2004 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -82,7 +81,7 @@ static store_key *get_store_key(THD *thd, static bool make_simple_join(JOIN *join,TABLE *tmp_table); static void make_outerjoin_info(JOIN *join); static bool make_join_select(JOIN *join,SQL_SELECT *select,COND *item); -static void make_join_readinfo(JOIN *join,uint options); +static void make_join_readinfo(JOIN *join, ulonglong options); static bool only_eq_ref_tables(JOIN *join, ORDER *order, table_map tables); static void update_depend_map(JOIN *join); static void update_depend_map(JOIN *join, ORDER *order); @@ -90,7 +89,7 @@ static ORDER *remove_const(JOIN *join,ORDER *first_order,COND *cond, bool change_list, bool *simple_order); static int return_zero_rows(JOIN *join, select_result *res,TABLE_LIST *tables, List<Item> &fields, bool send_row, - uint select_options, const char *info, + ulonglong select_options, const char *info, Item *having); static COND *build_equal_items(THD *thd, COND *cond, COND_EQUAL *inherited, @@ -110,11 +109,10 @@ static uint build_bitmap_for_nested_joins(List<TABLE_LIST> *join_list, static COND *optimize_cond(JOIN *join, COND *conds, List<TABLE_LIST> *join_list, Item::cond_result *cond_value); -static bool resolve_nested_join (TABLE_LIST *table); static bool const_expression_in_where(COND *conds,Item *item, Item **comp_item); static bool open_tmp_table(TABLE *table); static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param, - ulong options); + ulonglong options); static int do_select(JOIN *join,List<Item> *fields,TABLE *tmp_table, Procedure *proc); @@ -158,8 +156,8 @@ static int join_read_prev_same(READ_RECORD *info); static int join_read_prev(READ_RECORD *info); static int join_ft_read_first(JOIN_TAB *tab); static int join_ft_read_next(READ_RECORD *info); -static int join_read_always_key_or_null(JOIN_TAB *tab); -static int join_read_next_same_or_null(READ_RECORD *info); +int join_read_always_key_or_null(JOIN_TAB *tab); +int join_read_next_same_or_null(READ_RECORD *info); static COND *make_cond_for_table(COND *cond,table_map table, table_map used_table); static Item* part_of_refkey(TABLE *form,Field *field); @@ -270,6 +268,70 @@ bool handle_select(THD *thd, LEX *lex, select_result *result, /* + Fix fields referenced from inner selects. + + SYNOPSIS + fix_inner_refs() + thd Thread handle + all_fields List of all fields used in select + select Current select + ref_pointer_array Array of references to Items used in current select + + DESCRIPTION + The function fixes fields referenced from inner selects and + also fixes references (Item_ref objects) to these fields. Each field + is fixed as a usual hidden field of the current select - it is added + to the all_fields list and the pointer to it is saved in the + ref_pointer_array if latter is provided. + After the field has been fixed we proceed with fixing references + (Item_ref objects) to this field from inner subqueries. If the + ref_pointer_array is provided then Item_ref objects is set to + reference element in that array with the pointer to the field. + + RETURN + TRUE an error occured + FALSE ok +*/ + +bool +fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select, + Item **ref_pointer_array) +{ + Item_outer_ref *ref; + bool res= FALSE; + List_iterator<Item_outer_ref> ref_it(select->inner_refs_list); + while ((ref= ref_it++)) + { + Item_field *item= ref->outer_field; + /* + TODO: this field item already might be present in the select list. + In this case instead of adding new field item we could use an + existing one. The change will lead to less operations for copying fields, + smaller temporary tables and less data passed through filesort. + */ + if (ref_pointer_array) + { + int el= all_fields.elements; + ref_pointer_array[el]= (Item*)item; + /* Add the field item to the select list of the current select. */ + all_fields.push_front((Item*)item); + /* + If it's needed reset each Item_ref item that refers this field with + a new reference taken from ref_pointer_array. + */ + ref->ref= ref_pointer_array + el; + } + if (!ref->fixed && ref->fix_fields(thd, 0)) + { + res= TRUE; + break; + } + thd->used_tables|= item->used_tables(); + } + return res; +} + +/* Function to setup clauses without sum functions */ inline int setup_without_group(THD *thd, Item **ref_pointer_array, @@ -334,6 +396,7 @@ JOIN::prepare(Item ***rref_pointer_array, join_list= &select_lex->top_join_list; union_part= (unit_arg->first_select()->next_select() != 0); + thd->lex->current_select->is_item_list_lookup= 1; /* If we have already executed SELECT, then it have not sense to prevent its table from update (see unique_table()) @@ -393,9 +456,24 @@ JOIN::prepare(Item ***rref_pointer_array, select_lex->fix_prepare_information(thd, &conds, &having); + if (order) + { + ORDER *ord; + for (ord= order; ord; ord= ord->next) + { + Item *item= *ord->item; + if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) + item->split_sum_func(thd, ref_pointer_array, all_fields); + } + } + if (having && having->with_sum_func) having->split_sum_func2(thd, ref_pointer_array, all_fields, &having, TRUE); + if (select_lex->inner_refs_list.elements && + fix_inner_refs(thd, all_fields, select_lex, ref_pointer_array)) + DBUG_RETURN(-1); + if (select_lex->inner_sum_func_list) { Item_sum *end=select_lex->inner_sum_func_list; @@ -476,6 +554,9 @@ JOIN::prepare(Item ***rref_pointer_array, } } + if (!procedure && result && result->prepare(fields_list, unit_arg)) + goto err; /* purecov: inspected */ + /* Init join struct */ count_field_types(&tmp_table_param, all_fields, 0); ref_pointer_array_size= all_fields.elements*sizeof(Item*); @@ -489,9 +570,6 @@ JOIN::prepare(Item ***rref_pointer_array, goto err; } #endif - if (!procedure && result && result->prepare(fields_list, unit_arg)) - goto err; /* purecov: inspected */ - if (select_lex->olap == ROLLUP_TYPE && rollup_init()) goto err; if (alloc_func_list()) @@ -505,41 +583,87 @@ err: DBUG_RETURN(-1); /* purecov: inspected */ } + /* - test if it is known for optimisation IN subquery + Remove the predicates pushed down into the subquery - SYNOPSYS - JOIN::test_in_subselect - where - pointer for variable in which conditions should be - stored if subquery is known + SYNOPSIS + JOIN::remove_subq_pushed_predicates() + where IN Must be NULL + OUT The remaining WHERE condition, or NULL - RETURN - 1 - known - 0 - unknown + DESCRIPTION + Given that this join will be executed using (unique|index)_subquery, + without "checking NULL", remove the predicates that were pushed down + into the subquery. + + We can remove the equalities that will be guaranteed to be true by the + fact that subquery engine will be using index lookup. + + If the subquery compares scalar values, we can remove the condition that + was wrapped into trig_cond (it will be checked when needed by the subquery + engine) + + If the subquery compares row values, we need to keep the wrapped + equalities in the WHERE clause: when the left (outer) tuple has both NULL + and non-NULL values, we'll do a full table scan and will rely on the + equalities corresponding to non-NULL parts of left tuple to filter out + non-matching records. */ -bool JOIN::test_in_subselect(Item **where) +void JOIN::remove_subq_pushed_predicates(Item **where) { if (conds->type() == Item::FUNC_ITEM && ((Item_func *)this->conds)->functype() == Item_func::EQ_FUNC && ((Item_func *)conds)->arguments()[0]->type() == Item::REF_ITEM && ((Item_func *)conds)->arguments()[1]->type() == Item::FIELD_ITEM) { - join_tab->info= "Using index"; *where= 0; - return 1; + return; } if (conds->type() == Item::COND_ITEM && ((class Item_func *)this->conds)->functype() == Item_func::COND_AND_FUNC) { - if ((*where= remove_additional_cond(conds))) - join_tab->info= "Using index; Using where"; - else - join_tab->info= "Using index"; - return 1; + *where= remove_additional_cond(conds); + } +} + + +/* + Index lookup-based subquery: save some flags for EXPLAIN output + + SYNOPSIS + save_index_subquery_explain_info() + join_tab Subquery's join tab (there is only one as index lookup is + only used for subqueries that are single-table SELECTs) + where Subquery's WHERE clause + + DESCRIPTION + For index lookup-based subquery (i.e. one executed with + subselect_uniquesubquery_engine or subselect_indexsubquery_engine), + check its EXPLAIN output row should contain + "Using index" (TAB_INFO_FULL_SCAN_ON_NULL) + "Using Where" (TAB_INFO_USING_WHERE) + "Full scan on NULL key" (TAB_INFO_FULL_SCAN_ON_NULL) + and set appropriate flags in join_tab->packed_info. +*/ + +static void save_index_subquery_explain_info(JOIN_TAB *join_tab, Item* where) +{ + join_tab->packed_info= TAB_INFO_HAVE_VALUE; + if (join_tab->table->used_keys.is_set(join_tab->ref.key)) + join_tab->packed_info |= TAB_INFO_USING_INDEX; + if (where) + join_tab->packed_info |= TAB_INFO_USING_WHERE; + for (uint i = 0; i < join_tab->ref.key_parts; i++) + { + if (join_tab->ref.cond_guards[i]) + { + join_tab->packed_info |= TAB_INFO_FULL_SCAN_ON_NULL; + break; + } } - return 0; } @@ -626,7 +750,6 @@ JOIN::optimize() } { - Item::cond_result having_value; having= optimize_cond(this, having, join_list, &having_value); if (thd->net.report_error) { @@ -634,6 +757,10 @@ JOIN::optimize() DBUG_PRINT("error",("Error from optimize_cond")); DBUG_RETURN(1); } + if (select_lex->where) + select_lex->cond_value= cond_value; + if (select_lex->having) + select_lex->having_value= having_value; if (cond_value == Item::COND_FALSE || having_value == Item::COND_FALSE || (!unit->select_limit_cnt && !(select_options & OPTION_FOUND_ROWS))) @@ -672,23 +799,27 @@ JOIN::optimize() { int res; /* - opt_sum_query() returns -1 if no rows match to the WHERE conditions, - or 1 if all items were resolved, or 0, or an error number HA_ERR_... + opt_sum_query() returns HA_ERR_KEY_NOT_FOUND if no rows match + to the WHERE conditions, + or 1 if all items were resolved, + or 0, or an error number HA_ERR_... */ if ((res=opt_sum_query(select_lex->leaf_tables, all_fields, conds))) { - if (res > 1) - { - DBUG_PRINT("error",("Error from opt_sum_query")); - DBUG_RETURN(1); - } - if (res < 0) + if (res == HA_ERR_KEY_NOT_FOUND) { DBUG_PRINT("info",("No matching min/max row")); zero_result_cause= "No matching min/max row"; error=0; DBUG_RETURN(0); } + if (res > 1) + { + thd->fatal_error(); + error= res; + DBUG_PRINT("error",("Error from opt_sum_query")); + DBUG_RETURN(1); + } DBUG_PRINT("info",("Select tables optimized away")); zero_result_cause= "Select tables optimized away"; tables_list= 0; // All tables resolved @@ -790,6 +921,7 @@ JOIN::optimize() conds->update_used_tables(); DBUG_EXECUTE("where", print_where(conds, "after substitute_best_equal");); } + /* Permorm the the optimization on fields evaluation mentioned above for all on expressions. @@ -818,6 +950,13 @@ JOIN::optimize() { ORDER *org_order= order; order=remove_const(this, order,conds,1, &simple_order); + if (thd->net.report_error) + { + error= 1; + DBUG_PRINT("error",("Error from remove_const")); + DBUG_RETURN(1); + } + /* If we are using ORDER BY NULL or ORDER BY const_expression, return result in any order (even if we are using a GROUP BY) @@ -827,10 +966,11 @@ JOIN::optimize() } /* Check if we can optimize away GROUP BY/DISTINCT. - We can do that if there are no aggregate functions and the + We can do that if there are no aggregate functions, the fields in DISTINCT clause (if present) and/or columns in GROUP BY (if present) contain direct references to all key parts of - an unique index (in whatever order). + an unique index (in whatever order) and if the key parts of the + unique index cannot contain NULLs. Note that the unique keys for DISTINCT and GROUP BY should not be the same (as long as they are unique). @@ -925,6 +1065,12 @@ JOIN::optimize() group_list= remove_const(this, (old_group_list= group_list), conds, rollup.state == ROLLUP::STATE_NONE, &simple_group); + if (thd->net.report_error) + { + error= 1; + DBUG_PRINT("error",("Error from remove_const")); + DBUG_RETURN(1); + } if (old_group_list && !group_list) select_distinct= 0; } @@ -941,6 +1087,12 @@ JOIN::optimize() { group_list= procedure->group= remove_const(this, procedure->group, conds, 1, &simple_group); + if (thd->net.report_error) + { + error= 1; + DBUG_PRINT("error",("Error from remove_const")); + DBUG_RETURN(1); + } calc_group_buffer(this, group_list); } @@ -996,53 +1148,47 @@ JOIN::optimize() if (join_tab[0].type == JT_EQ_REF && join_tab[0].ref.items[0]->name == in_left_expr_name) { - if (test_in_subselect(&where)) - { - join_tab[0].type= JT_UNIQUE_SUBQUERY; - error= 0; - DBUG_RETURN(unit->item-> - change_engine(new - subselect_uniquesubquery_engine(thd, - join_tab, - unit->item, - where))); - } + remove_subq_pushed_predicates(&where); + save_index_subquery_explain_info(join_tab, where); + join_tab[0].type= JT_UNIQUE_SUBQUERY; + error= 0; + DBUG_RETURN(unit->item-> + change_engine(new + subselect_uniquesubquery_engine(thd, + join_tab, + unit->item, + where))); } else if (join_tab[0].type == JT_REF && join_tab[0].ref.items[0]->name == in_left_expr_name) { - if (test_in_subselect(&where)) - { - join_tab[0].type= JT_INDEX_SUBQUERY; - error= 0; - DBUG_RETURN(unit->item-> - change_engine(new - subselect_indexsubquery_engine(thd, - join_tab, - unit->item, - where, - 0))); - } + remove_subq_pushed_predicates(&where); + save_index_subquery_explain_info(join_tab, where); + join_tab[0].type= JT_INDEX_SUBQUERY; + error= 0; + DBUG_RETURN(unit->item-> + change_engine(new + subselect_indexsubquery_engine(thd, + join_tab, + unit->item, + where, + NULL, + 0))); } } else if (join_tab[0].type == JT_REF_OR_NULL && join_tab[0].ref.items[0]->name == in_left_expr_name && - having->type() == Item::FUNC_ITEM && - ((Item_func *) having)->functype() == - Item_func::ISNOTNULLTEST_FUNC) + having->name == in_having_cond) { join_tab[0].type= JT_INDEX_SUBQUERY; error= 0; - - if ((conds= remove_additional_cond(conds))) - join_tab->info= "Using index; Using where"; - else - join_tab->info= "Using index"; - + conds= remove_additional_cond(conds); + save_index_subquery_explain_info(join_tab, conds); DBUG_RETURN(unit->item-> change_engine(new subselect_indexsubquery_engine(thd, join_tab, unit->item, conds, + having, 1))); } @@ -1274,14 +1420,14 @@ JOIN::reinit() exec_tmp_table1->file->extra(HA_EXTRA_RESET_STATE); exec_tmp_table1->file->delete_all_rows(); free_io_cache(exec_tmp_table1); - filesort_free_buffers(exec_tmp_table1); + filesort_free_buffers(exec_tmp_table1,0); } if (exec_tmp_table2) { exec_tmp_table2->file->extra(HA_EXTRA_RESET_STATE); exec_tmp_table2->file->delete_all_rows(); free_io_cache(exec_tmp_table2); - filesort_free_buffers(exec_tmp_table2); + filesort_free_buffers(exec_tmp_table2,0); } if (items0) set_items_ref_array(items0); @@ -1444,7 +1590,8 @@ JOIN::exec() curr_join->examined_rows= 0; if ((curr_join->select_lex->options & OPTION_SCHEMA_TABLE) && - get_schema_tables_result(curr_join)) + !thd->lex->describe && + get_schema_tables_result(curr_join, PROCESSED_BY_JOIN_EXEC)) { DBUG_VOID_RETURN; } @@ -1960,7 +2107,7 @@ bool mysql_select(THD *thd, Item ***rref_pointer_array, TABLE_LIST *tables, uint wild_num, List<Item> &fields, COND *conds, uint og_num, ORDER *order, ORDER *group, - Item *having, ORDER *proc_param, ulong select_options, + Item *having, ORDER *proc_param, ulonglong select_options, select_result *result, SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex) { @@ -2149,7 +2296,12 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds, s->needed_reg.init(); table_vector[i]=s->table=table=tables->table; table->pos_in_table_list= tables; - table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);// record count + error= table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); + if(error) + { + table->file->print_error(error, MYF(0)); + DBUG_RETURN(1); + } table->quick_keys.clear_all(); table->reginfo.join_tab=s; table->reginfo.not_exists_optimize=0; @@ -2162,7 +2314,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds, s->key_dependent= 0; if (tables->schema_table) table->file->stats.records= 2; - table->quick_condition_rows= table->file->records(); + table->quick_condition_rows= table->file->stats.records; s->on_expr_ref= &tables->on_expr; if (*s->on_expr_ref) @@ -2200,7 +2352,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds, continue; } #ifdef WITH_PARTITION_STORAGE_ENGINE - bool no_partitions_used= table->no_partitions_used; + const bool no_partitions_used= table->no_partitions_used; #else const bool no_partitions_used= FALSE; #endif @@ -2298,8 +2450,18 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds, substitution of a const table the key value happens to be null then we can state that there are no matches for this equi-join. */ - if ((keyuse= s->keyuse) && *s->on_expr_ref) + if ((keyuse= s->keyuse) && *s->on_expr_ref && !s->embedding_map) { + /* + When performing an outer join operation if there are no matching rows + for the single row of the outer table all the inner tables are to be + null complemented and thus considered as constant tables. + Here we apply this consideration to the case of outer join operations + with a single inner table only because the case with nested tables + would require a more thorough analysis. + TODO. Apply single row substitution to null complemented inner tables + for nested outer join operations. + */ while (keyuse->table == table) { if (!(keyuse->val->used_tables() & ~join->const_table_map) && @@ -2412,14 +2574,14 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds, for( ; sargables->field ; sargables++) { Field *field= sargables->field; - JOIN_TAB *stat= field->table->reginfo.join_tab; + JOIN_TAB *join_tab= field->table->reginfo.join_tab; key_map possible_keys= field->key_start; possible_keys.intersect(field->table->keys_in_use_for_query); bool is_const= 1; - for (uint i=0; i< sargables->num_values; i++) - is_const&= sargables->arg_value[i]->const_item(); + for (uint j=0; j < sargables->num_values; j++) + is_const&= sargables->arg_value[j]->const_item(); if (is_const) - stat[0].const_keys.merge(possible_keys); + join_tab[0].const_keys.merge(possible_keys); } } @@ -2540,6 +2702,7 @@ typedef struct key_field_t { // Used when finding key fields when val IS NULL. */ bool null_rejecting; + bool *cond_guard; /* See KEYUSE::cond_guard */ } KEY_FIELD; /* Values in optimize */ @@ -2805,15 +2968,9 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, /* We can't use indexes if the effective collation of the operation differ from the field collation. - - We also cannot use index on a text column, as the column may - contain 'x' 'x\t' 'x ' and 'read_next_same' will stop after - 'x' when searching for WHERE col='x ' */ if (field->cmp_type() == STRING_RESULT && - (((Field_str*)field)->charset() != cond->compare_collation() || - ((*value)->type() != Item::NULL_ITEM && - (field->flags & BLOB_FLAG) && !field->binary()))) + ((Field_str*)field)->charset() != cond->compare_collation()) return; } } @@ -2841,6 +2998,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, cond->functype() == Item_func::MULT_EQUAL_FUNC) && ((*value)->type() == Item::FIELD_ITEM) && ((Item_field*)*value)->field->maybe_null()); + (*key_fields)->cond_guard= NULL; (*key_fields)++; } @@ -2899,7 +3057,7 @@ add_key_equal_fields(KEY_FIELD **key_fields, uint and_level, } static void -add_key_fields(KEY_FIELD **key_fields,uint *and_level, +add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, COND *cond, table_map usable_tables, SARGABLE_PARAM **sargables) { @@ -2912,28 +3070,57 @@ add_key_fields(KEY_FIELD **key_fields,uint *and_level, { Item *item; while ((item=li++)) - add_key_fields(key_fields,and_level,item,usable_tables,sargables); + add_key_fields(join, key_fields, and_level, item, usable_tables, + sargables); for (; org_key_fields != *key_fields ; org_key_fields++) org_key_fields->level= *and_level; } else { (*and_level)++; - add_key_fields(key_fields,and_level,li++,usable_tables,sargables); + add_key_fields(join, key_fields, and_level, li++, usable_tables, + sargables); Item *item; while ((item=li++)) { KEY_FIELD *start_key_fields= *key_fields; (*and_level)++; - add_key_fields(key_fields,and_level,item,usable_tables,sargables); + add_key_fields(join, key_fields, and_level, item, usable_tables, + sargables); *key_fields=merge_key_fields(org_key_fields,start_key_fields, *key_fields,++(*and_level)); } } return; } - /* If item is of type 'field op field/constant' add it to key_fields */ + /* + Subquery optimization: Conditions that are pushed down into subqueries + are wrapped into Item_func_trig_cond. We process the wrapped condition + but need to set cond_guard for KEYUSE elements generated from it. + */ + { + if (cond->type() == Item::FUNC_ITEM && + ((Item_func*)cond)->functype() == Item_func::TRIG_COND_FUNC) + { + Item *cond_arg= ((Item_func*)cond)->arguments()[0]; + if (!join->group_list && !join->order && + join->unit->item && + join->unit->item->substype() == Item_subselect::IN_SUBS && + !join->unit->first_select()->next_select()) + { + KEY_FIELD *save= *key_fields; + add_key_fields(join, key_fields, and_level, cond_arg, usable_tables, + sargables); + // Indicate that this ref access candidate is for subquery lookup: + for (; save != *key_fields; save++) + save->cond_guard= ((Item_func_trig_cond*)cond)->get_trig_var(); + } + return; + } + } + + /* If item is of type 'field op field/constant' add it to key_fields */ if (cond->type() != Item::FUNC_ITEM) return; Item_func *cond_func= (Item_func*) cond; @@ -3107,6 +3294,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field) keyuse.used_tables=key_field->val->used_tables(); keyuse.optimize= key_field->optimize & KEY_OPTIMIZE_REF_OR_NULL; keyuse.null_rejecting= key_field->null_rejecting; + keyuse.cond_guard= key_field->cond_guard; VOID(insert_dynamic(keyuse_array,(gptr) &keyuse)); } } @@ -3229,7 +3417,7 @@ sort_keyuse(KEYUSE *a,KEYUSE *b) Here we can add 'ref' access candidates for t1 and t2, but not for t3. */ -static void add_key_fields_for_nj(TABLE_LIST *nested_join_table, +static void add_key_fields_for_nj(JOIN *join, TABLE_LIST *nested_join_table, KEY_FIELD **end, uint *and_level, SARGABLE_PARAM **sargables) { @@ -3241,12 +3429,13 @@ static void add_key_fields_for_nj(TABLE_LIST *nested_join_table, while ((table= li++)) { if (table->nested_join) - add_key_fields_for_nj(table, end, and_level, sargables); + add_key_fields_for_nj(join, table, end, and_level, sargables); else if (!table->on_expr) tables |= table->table->map; } - add_key_fields(end, and_level, nested_join_table->on_expr, tables, sargables); + add_key_fields(join, end, and_level, nested_join_table->on_expr, tables, + sargables); } @@ -3321,7 +3510,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, return TRUE; if (cond) { - add_key_fields(&end,&and_level,cond,normal_tables,sargables); + add_key_fields(join_tab->join, &end, &and_level, cond, normal_tables, + sargables); for (; field != end ; field++) { add_key_part(keyuse,field); @@ -3343,8 +3533,9 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, into account as well. */ if (*join_tab[i].on_expr_ref) - add_key_fields(&end,&and_level,*join_tab[i].on_expr_ref, - join_tab[i].table->map,sargables); + add_key_fields(join_tab->join, &end, &and_level, + *join_tab[i].on_expr_ref, + join_tab[i].table->map, sargables); } /* Process ON conditions for the nested joins */ @@ -3354,7 +3545,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, while ((table= li++)) { if (table->nested_join) - add_key_fields_for_nj(table, &end, &and_level, sargables); + add_key_fields_for_nj(join_tab->join, table, &end, &and_level, + sargables); } } @@ -3379,16 +3571,16 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, */ if (keyuse->elements) { - KEYUSE end,*prev,*save_pos,*use; + KEYUSE key_end,*prev,*save_pos,*use; qsort(keyuse->buffer,keyuse->elements,sizeof(KEYUSE), (qsort_cmp) sort_keyuse); - bzero((char*) &end,sizeof(end)); /* Add for easy testing */ - VOID(insert_dynamic(keyuse,(gptr) &end)); + bzero((char*) &key_end,sizeof(key_end)); /* Add for easy testing */ + VOID(insert_dynamic(keyuse,(gptr) &key_end)); use=save_pos=dynamic_element(keyuse,0,KEYUSE*); - prev=&end; + prev= &key_end; found_eq_constant=0; for (i=0 ; i < keyuse->elements-1 ; i++,use++) { @@ -3406,7 +3598,11 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, continue; } - *save_pos= *use; +#ifdef HAVE_purify + /* Valgrind complains about overlapped memcpy when save_pos==use. */ + if (save_pos != use) +#endif + *save_pos= *use; prev=use; found_eq_constant= !use->used_tables; /* Save ptr to first use */ @@ -3416,7 +3612,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, save_pos++; } i=(uint) (save_pos-(KEYUSE*) keyuse->buffer); - VOID(set_dynamic(keyuse,(gptr) &end,i)); + VOID(set_dynamic(keyuse,(gptr) &key_end,i)); keyuse->elements=i; } return FALSE; @@ -3598,10 +3794,9 @@ best_access_path(JOIN *join, double best= DBL_MAX; double best_time= DBL_MAX; double records= DBL_MAX; - table_map best_ref_depends_map; + table_map best_ref_depends_map= 0; double tmp; ha_rows rec; - DBUG_ENTER("best_access_path"); if (s->keyuse) @@ -3649,12 +3844,12 @@ best_access_path(JOIN *join, if (!(keyuse->used_tables & ~join->const_table_map)) const_part|= keyuse->keypart_map; - double tmp= prev_record_reads(join, idx, (found_ref | + double tmp2= prev_record_reads(join, idx, (found_ref | keyuse->used_tables)); - if (tmp < best_prev_record_reads) + if (tmp2 < best_prev_record_reads) { best_part_found_ref= keyuse->used_tables & ~join->const_table_map; - best_prev_record_reads= tmp; + best_prev_record_reads= tmp2; } if (rec > keyuse->ref_table_rows) rec= keyuse->ref_table_rows; @@ -4133,7 +4328,7 @@ choose_plan(JOIN *join, table_map join_tables) { uint search_depth= join->thd->variables.optimizer_search_depth; uint prune_level= join->thd->variables.optimizer_prune_level; - bool straight_join= join->select_options & SELECT_STRAIGHT_JOIN; + bool straight_join= test(join->select_options & SELECT_STRAIGHT_JOIN); DBUG_ENTER("choose_plan"); join->cur_embedding_map= 0; @@ -4735,8 +4930,6 @@ static void find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, double read_time) { - ha_rows rec; - double tmp; THD *thd= join->thd; if (!rest_tables) { @@ -5064,7 +5257,8 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, if (!(j->ref.key_buff= (byte*) thd->calloc(ALIGN_SIZE(length)*2)) || !(j->ref.key_copy= (store_key**) thd->alloc((sizeof(store_key*) * (keyparts+1)))) || - !(j->ref.items= (Item**) thd->alloc(sizeof(Item*)*keyparts))) + !(j->ref.items= (Item**) thd->alloc(sizeof(Item*)*keyparts)) || + !(j->ref.cond_guards= (bool**) thd->alloc(sizeof(uint*)*keyparts))) { DBUG_RETURN(TRUE); } @@ -5079,6 +5273,8 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, if (ftkey) { j->ref.items[0]=((Item_func*)(keyuse->val))->key_item(); + /* Predicates pushed down into subquery can't be used FT access */ + j->ref.cond_guards[0]= NULL; if (keyuse->used_tables) DBUG_RETURN(TRUE); // not supported yet. SerG @@ -5095,6 +5291,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, uint maybe_null= test(keyinfo->key_part[i].null_bit); j->ref.items[i]=keyuse->val; // Save for cond removal + j->ref.cond_guards[i]= keyuse->cond_guard; if (keyuse->null_rejecting) j->ref.null_rejecting |= 1 << i; keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables; @@ -5168,13 +5365,15 @@ get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables, key_part->length, keyuse->val); } - else if (keyuse->val->type() == Item::FIELD_ITEM) + else if (keyuse->val->type() == Item::FIELD_ITEM || + (keyuse->val->type() == Item::REF_ITEM && + ((Item_ref*)keyuse->val)->ref_type() == Item_ref::OUTER_REF) ) return new store_key_field(thd, key_part->field, key_buff + maybe_null, maybe_null ? key_buff : 0, key_part->length, - ((Item_field*) keyuse->val)->field, + ((Item_field*) keyuse->val->real_item())->field, keyuse->val->full_name()); return new store_key_item(thd, key_part->field, @@ -5822,37 +6021,42 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) */ COND *on_expr= *first_inner_tab->on_expr_ref; - table_map used_tables= join->const_table_map | - OUTER_REF_TABLE_BIT | RAND_TABLE_BIT; + table_map used_tables2= (join->const_table_map | + OUTER_REF_TABLE_BIT | RAND_TABLE_BIT); for (tab= join->join_tab+join->const_tables; tab <= last_tab ; tab++) { current_map= tab->table->map; - used_tables|= current_map; - COND *tmp= make_cond_for_table(on_expr, used_tables, current_map); - if (tmp) + used_tables2|= current_map; + COND *tmp_cond= make_cond_for_table(on_expr, used_tables2, + current_map); + if (tmp_cond) { JOIN_TAB *cond_tab= tab < first_inner_tab ? first_inner_tab : tab; /* First add the guards for match variables of all embedding outer join operations. */ - if (!(tmp= add_found_match_trig_cond(cond_tab->first_inner, - tmp, first_inner_tab))) + if (!(tmp_cond= add_found_match_trig_cond(cond_tab->first_inner, + tmp_cond, + first_inner_tab))) DBUG_RETURN(1); /* Now add the guard turning the predicate off for the null complemented row. */ DBUG_PRINT("info", ("Item_func_trig_cond")); - tmp= new Item_func_trig_cond(tmp, - &first_inner_tab->not_null_compl); - DBUG_PRINT("info", ("Item_func_trig_cond 0x%lx", (ulong) tmp)); - if (tmp) - tmp->quick_fix_field(); + tmp_cond= new Item_func_trig_cond(tmp_cond, + &first_inner_tab-> + not_null_compl); + DBUG_PRINT("info", ("Item_func_trig_cond 0x%lx", + (ulong) tmp_cond)); + if (tmp_cond) + tmp_cond->quick_fix_field(); /* Add the predicate to other pushed down predicates */ DBUG_PRINT("info", ("Item_cond_and")); - cond_tab->select_cond= !cond_tab->select_cond ? tmp : - new Item_cond_and(cond_tab->select_cond,tmp); + cond_tab->select_cond= !cond_tab->select_cond ? tmp_cond : + new Item_cond_and(cond_tab->select_cond, + tmp_cond); DBUG_PRINT("info", ("Item_cond_and 0x%lx", (ulong)cond_tab->select_cond)); if (!cond_tab->select_cond) @@ -5868,7 +6072,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) } static void -make_join_readinfo(JOIN *join, uint options) +make_join_readinfo(JOIN *join, ulonglong options) { uint i; bool statistics= test(!(join->select_options & SELECT_DESCRIBE)); @@ -6171,7 +6375,7 @@ void JOIN_TAB::cleanup() void JOIN::join_free() { - SELECT_LEX_UNIT *unit; + SELECT_LEX_UNIT *tmp_unit; SELECT_LEX *sl; /* Optimization: if not EXPLAIN and we are done with the JOIN, @@ -6183,8 +6387,10 @@ void JOIN::join_free() cleanup(full); - for (unit= select_lex->first_inner_unit(); unit; unit= unit->next_unit()) - for (sl= unit->first_select(); sl; sl= sl->next_select()) + for (tmp_unit= select_lex->first_inner_unit(); + tmp_unit; + tmp_unit= tmp_unit->next_unit()) + for (sl= tmp_unit->first_select(); sl; sl= sl->next_select()) { Item_subselect *subselect= sl->master_unit()->item; bool full_local= full && (!subselect || subselect->is_evaluated()); @@ -6250,7 +6456,7 @@ void JOIN::cleanup(bool full) if (tables > const_tables) // Test for not-const tables { free_io_cache(table[const_tables]); - filesort_free_buffers(table[const_tables]); + filesort_free_buffers(table[const_tables],full); } if (full) @@ -6483,6 +6689,8 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, *simple_order=0; // Must do a temp table to sort else if (!(order_tables & not_const_tables)) { + if (order->item[0]->with_subselect) + order->item[0]->val_str(&order->item[0]->str_value); DBUG_PRINT("info",("removing: %s", order->item[0]->full_name())); continue; // skip const item } @@ -6525,7 +6733,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, static int return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables, - List<Item> &fields, bool send_row, uint select_options, + List<Item> &fields, bool send_row, ulonglong select_options, const char *info, Item *having) { DBUG_ENTER("return_zero_rows"); @@ -6820,9 +7028,9 @@ static bool check_simple_equality(Item *left_item, Item *right_item, else { /* None of the fields was found in multiple equalities */ - Item_equal *item= new Item_equal((Item_field *) left_item, - (Item_field *) right_item); - cond_equal->current_level.push_back(item); + Item_equal *item_equal= new Item_equal((Item_field *) left_item, + (Item_field *) right_item); + cond_equal->current_level.push_back(item_equal); } } return TRUE; @@ -6929,8 +7137,8 @@ static bool check_row_equality(Item *left_row, Item_row *right_row, for (uint i= 0 ; i < n; i++) { bool is_converted; - Item *left_item= left_row->el(i); - Item *right_item= right_row->el(i); + Item *left_item= left_row->element_index(i); + Item *right_item= right_row->element_index(i); if (left_item->type() == Item::ROW_ITEM && right_item->type() == Item::ROW_ITEM) is_converted= check_row_equality((Item_row *) left_item, @@ -7076,7 +7284,6 @@ static COND *build_equal_items_for_cond(COND *cond, Item_equal *item_equal; uint members; COND_EQUAL cond_equal; - COND *new_cond; cond_equal.upper_levels= inherited; if (cond->type() == Item::COND_ITEM) @@ -7321,14 +7528,15 @@ static COND *build_equal_items(THD *thd, COND *cond, { if (table->on_expr) { - List<TABLE_LIST> *join_list= table->nested_join ? - &table->nested_join->join_list : NULL; + List<TABLE_LIST> *nested_join_list= table->nested_join ? + &table->nested_join->join_list : NULL; /* We can modify table->on_expr because its old value will be restored before re-execution of PS/SP. */ table->on_expr= build_equal_items(thd, table->on_expr, inherited, - join_list, &table->cond_equal); + nested_join_list, + &table->cond_equal); } } } @@ -7580,6 +7788,10 @@ static COND* substitute_for_best_equal_field(COND *cond, break; } } + if (cond->type() == Item::COND_ITEM && + !((Item_cond*)cond)->argument_list()->elements) + cond= new Item_int((int32)cond->val_bool()); + } else if (cond->type() == Item::FUNC_ITEM && ((Item_cond*) cond)->functype() == Item_func::MULT_EQUAL_FUNC) @@ -7643,6 +7855,22 @@ static void update_const_equal_items(COND *cond, JOIN_TAB *tab) key_map possible_keys= field->key_start; possible_keys.intersect(field->table->keys_in_use_for_query); stat[0].const_keys.merge(possible_keys); + + /* + For each field in the multiple equality (for which we know that it + is a constant) we have to find its corresponding key part, and set + that key part in const_key_parts. + */ + if (!possible_keys.is_clear_all()) + { + TABLE *tab= field->table; + KEYUSE *use; + for (use= stat->keyuse; use && use->table == tab; use++) + if (possible_keys.is_set(use->key) && + tab->key_info[use->key].key_part[use->keypart].field == + field) + tab->const_key_parts[use->key]|= use->keypart_map; + } } } } @@ -7685,7 +7913,7 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list, value->result_type() != STRING_RESULT || left_item->collation.collation == value->collation.collation)) { - Item *tmp=value->new_item(); + Item *tmp=value->clone_item(); tmp->collation.set(right_item->collation); if (tmp) @@ -7709,7 +7937,7 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list, value->result_type() != STRING_RESULT || right_item->collation.collation == value->collation.collation)) { - Item *tmp=value->new_item(); + Item *tmp= value->clone_item(); tmp->collation.set(left_item->collation); if (tmp) @@ -7737,7 +7965,7 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list, SYNOPSIS remove_additional_cond() - conds - condition for processing + conds Condition for processing RETURN VALUES new conditions @@ -8120,7 +8348,7 @@ static uint build_bitmap_for_nested_joins(List<TABLE_LIST> *join_list, */ if (nested_join->join_list.elements != 1) { - nested_join->nj_map= 1 << first_unused++; + nested_join->nj_map= (nested_join_map) 1 << first_unused++; first_unused= build_bitmap_for_nested_joins(&nested_join->join_list, first_unused); } @@ -8330,7 +8558,6 @@ optimize_cond(JOIN *join, COND *conds, List<TABLE_LIST> *join_list, Item::cond_result *cond_value) { THD *thd= join->thd; - SELECT_LEX *select= thd->lex->current_select; DBUG_ENTER("optimize_cond"); if (!conds) @@ -8480,8 +8707,8 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) thd->substitute_null_with_insert_id= FALSE; } /* fix to replace 'NULL' dates with '0' (shreeve@uci.edu) */ - else if (((field->type() == FIELD_TYPE_DATE) || - (field->type() == FIELD_TYPE_DATETIME)) && + else if (((field->type() == MYSQL_TYPE_DATE) || + (field->type() == MYSQL_TYPE_DATETIME)) && (field->flags & NOT_NULL_FLAG) && !field->table->maybe_null) { @@ -8524,6 +8751,46 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) return cond; // Point at next and level } +/* + Check if equality can be used in removing components of GROUP BY/DISTINCT + + SYNOPSIS + test_if_equality_guarantees_uniqueness() + l the left comparison argument (a field if any) + r the right comparison argument (a const of any) + + DESCRIPTION + Checks if an equality predicate can be used to take away + DISTINCT/GROUP BY because it is known to be true for exactly one + distinct value (e.g. <expr> == <const>). + Arguments must be of the same type because e.g. + <string_field> = <int_const> may match more than 1 distinct value from + the column. + We must take into consideration and the optimization done for various + string constants when compared to dates etc (see Item_int_with_ref) as + well as the collation of the arguments. + + RETURN VALUE + TRUE can be used + FALSE cannot be used +*/ +static bool +test_if_equality_guarantees_uniqueness(Item *l, Item *r) +{ + return r->const_item() && + /* elements must be of the same result type */ + (r->result_type() == l->result_type() || + /* or dates compared to longs */ + (((l->type() == Item::FIELD_ITEM && + ((Item_field *)l)->field->can_be_compared_as_longlong()) || + (l->type() == Item::FUNC_ITEM && + ((Item_func *)l)->result_as_longlong())) && + r->result_type() == INT_RESULT)) + /* and must have the same collation if compared as strings */ + && (l->result_type() != STRING_RESULT || + l->collation.collation == r->collation.collation); +} + /* Return 1 if the item is a const value in all the WHERE clause */ @@ -8560,7 +8827,7 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item) Item *right_item= ((Item_func*) cond)->arguments()[1]; if (left_item->eq(comp_item,1)) { - if (right_item->const_item()) + if (test_if_equality_guarantees_uniqueness (left_item, right_item)) { if (*const_item) return right_item->eq(*const_item, 1); @@ -8570,7 +8837,7 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item) } else if (right_item->eq(comp_item,1)) { - if (left_item->const_item()) + if (test_if_equality_guarantees_uniqueness (right_item, left_item)) { if (*const_item) return left_item->eq(*const_item, 1); @@ -8642,6 +8909,8 @@ Field *create_tmp_field_from_field(THD *thd, Field *org_field, if (org_field->type() == MYSQL_TYPE_VAR_STRING || org_field->type() == MYSQL_TYPE_VARCHAR) table->s->db_create_options|= HA_OPTION_PACK_RECORD; + else if (org_field->type() == FIELD_TYPE_DOUBLE) + ((Field_double *) new_field)->not_fixed= TRUE; } return new_field; } @@ -8682,7 +8951,7 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, switch (item->result_type()) { case REAL_RESULT: new_field= new Field_double(item->max_length, maybe_null, - item->name, item->decimals); + item->name, item->decimals, TRUE); break; case INT_RESULT: /* Select an integer type with the minimal fit precision */ @@ -8702,7 +8971,8 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, type they needed to be handled separately. */ if ((type= item->field_type()) == MYSQL_TYPE_DATETIME || - type == MYSQL_TYPE_TIME || type == MYSQL_TYPE_DATE) + type == MYSQL_TYPE_TIME || type == MYSQL_TYPE_DATE || + type == MYSQL_TYPE_TIMESTAMP) new_field= item->tmp_table_field_from_field_type(table, 1); /* Make sure that the blob fits into a Field_varstring which has @@ -8716,6 +8986,7 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, item->collation.collation); else new_field= item->make_string_field(table); + new_field->set_derivation(item->collation.derivation); break; case DECIMAL_RESULT: new_field= new Field_new_decimal(item->max_length, maybe_null, item->name, @@ -8817,9 +9088,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, Item *orig_item= 0; if (type != Item::FIELD_ITEM && - item->real_item()->type() == Item::FIELD_ITEM && - (item->type() != Item::REF_ITEM || - !((Item_ref *) item)->depended_from)) + item->real_item()->type() == Item::FIELD_ITEM) { orig_item= item; item= item->real_item(); @@ -8855,7 +9124,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, field->result_field= result; } else if (table_cant_handle_bit_fields && field->field->type() == - FIELD_TYPE_BIT) + MYSQL_TYPE_BIT) { *from_field= field->field; result= create_tmp_field_from_item(thd, item, table, copy_func, @@ -8901,7 +9170,9 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, (make_copy_field ? 0 : copy_func), modify_item, convert_blob_length); case Item::TYPE_HOLDER: - return ((Item_type_holder *)item)->make_field_by_type(table); + result= ((Item_type_holder *)item)->make_field_by_type(table); + result->set_derivation(item->collation.derivation); + return result; default: // Dosen't have to be stored return 0; } @@ -8979,7 +9250,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, uint hidden_null_count, hidden_null_pack_length, hidden_field_count; uint blob_count,group_null_items, string_count; uint temp_pool_slot=MY_BIT_NONE; - ulong reclength, string_total_length, fieldnr= 0; + uint fieldnr= 0; + ulong reclength, string_total_length; bool using_unique_constraint= 0; bool use_packed_rows= 0; bool not_all_columns= !(select_options & TMP_TABLE_ALL_COLUMNS); @@ -9170,7 +9442,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, *blob_field++= fieldnr; blob_count++; } - if (new_field->type() == FIELD_TYPE_BIT) + if (new_field->type() == MYSQL_TYPE_BIT) total_uneven_bit_length+= new_field->field_length & 7; *(reg_field++)= new_field; if (new_field->real_type() == MYSQL_TYPE_STRING || @@ -9232,7 +9504,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, reclength+=new_field->pack_length(); if (!(new_field->flags & NOT_NULL_FLAG)) null_count++; - if (new_field->type() == FIELD_TYPE_BIT) + if (new_field->type() == MYSQL_TYPE_BIT) total_uneven_bit_length+= new_field->field_length & 7; if (new_field->flags & BLOB_FLAG) { @@ -9377,7 +9649,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, } else field->move_field((char*) pos,(uchar*) 0,0); - if (field->type() == FIELD_TYPE_BIT) + if (field->type() == MYSQL_TYPE_BIT) { /* We have to reserve place for extra bits among null bits */ ((Field_bit*) field)->set_bit_ptr(null_flags + null_count / 8, @@ -9442,13 +9714,14 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, param->recinfo=recinfo; store_record(table,s->default_values); // Make empty default record - if (thd->variables.tmp_table_size == ~(ulong) 0) // No limit + if (thd->variables.tmp_table_size == ~ (ulonglong) 0) // No limit share->max_rows= ~(ha_rows) 0; else - share->max_rows= (((share->db_type == heap_hton) ? - min(thd->variables.tmp_table_size, - thd->variables.max_heap_table_size) : - thd->variables.tmp_table_size)/ share->reclength); + share->max_rows= (ha_rows) (((share->db_type == heap_hton) ? + min(thd->variables.tmp_table_size, + thd->variables.max_heap_table_size) : + thd->variables.tmp_table_size) / + share->reclength); set_if_bigger(share->max_rows,1); // For dummy start options /* Push the LIMIT clause to the temporary table creation, so that we @@ -9481,7 +9754,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, bool maybe_null=(*cur_group->item)->maybe_null; key_part_info->null_bit=0; key_part_info->field= field; - key_part_info->offset= field->offset(); + key_part_info->offset= field->offset(table->record[0]); key_part_info->length= (uint16) field->key_length(); key_part_info->type= (uint8) field->key_type(); key_part_info->key_type = @@ -9578,7 +9851,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, { key_part_info->null_bit=0; key_part_info->field= *reg_field; - key_part_info->offset= (*reg_field)->offset(); + key_part_info->offset= (*reg_field)->offset(table->record[0]); key_part_info->length= (uint16) (*reg_field)->pack_length(); key_part_info->type= (uint8) (*reg_field)->key_type(); key_part_info->key_type = @@ -9760,7 +10033,7 @@ static bool open_tmp_table(TABLE *table) static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param, - ulong options) + ulonglong options) { int error; MI_KEYDEF keydef; @@ -10099,7 +10372,8 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) enum_nested_loop_state error= NESTED_LOOP_OK; JOIN_TAB *join_tab; DBUG_ENTER("do_select"); - + LINT_INIT(join_tab); + join->procedure=procedure; join->tmp_table= table; /* Save for easy recursion */ join->fields= fields; @@ -10129,9 +10403,9 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) */ if (!join->conds || join->conds->val_int()) { - error= (*end_select)(join,join_tab,0); + error= (*end_select)(join, 0, 0); if (error == NESTED_LOOP_OK || error == NESTED_LOOP_QUERY_LIMIT) - error= (*end_select)(join,join_tab,1); + error= (*end_select)(join, 0, 1); } else if (join->send_row_on_empty_set()) { @@ -10143,7 +10417,6 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) else { DBUG_ASSERT(join->tables); - DBUG_ASSERT(join_tab); error= sub_select(join,join_tab,0); if (error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS) error= sub_select(join,join_tab,1); @@ -10169,7 +10442,7 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) if (join->result->send_eof()) rc= 1; // Don't send error } - DBUG_PRINT("info",("%ld records output",join->send_records)); + DBUG_PRINT("info",("%ld records output", (long) join->send_records)); } else rc= -1; @@ -10522,6 +10795,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, */ join->examined_rows++; join->thd->row_count++; + join_tab->read_record.file->unlock_row(); } return NESTED_LOOP_OK; } @@ -10687,7 +10961,7 @@ int report_error(TABLE *table, int error) */ if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT) sql_print_error("Got error %d when reading table '%s'", - error, table->s->path); + error, table->s->path.str); table->file->print_error(error,MYF(0)); return 1; } @@ -11001,6 +11275,15 @@ join_init_quick_read_record(JOIN_TAB *tab) } +int rr_sequential(READ_RECORD *info); +int init_read_record_seq(JOIN_TAB *tab) +{ + tab->read_record.read_record= rr_sequential; + if (tab->read_record.file->ha_rnd_init(1)) + return 1; + return (*tab->read_record.read_record)(&tab->read_record); +} + static int test_if_quick_select(JOIN_TAB *tab) { @@ -11130,7 +11413,7 @@ join_ft_read_next(READ_RECORD *info) Reading of key with key reference and one part that may be NULL */ -static int +int join_read_always_key_or_null(JOIN_TAB *tab) { int res; @@ -11146,7 +11429,7 @@ join_read_always_key_or_null(JOIN_TAB *tab) } -static int +int join_read_next_same_or_null(READ_RECORD *info) { int error; @@ -11664,7 +11947,7 @@ static bool test_if_ref(Item_field *left_item,Item *right_item) if (field->binary() && field->real_type() != MYSQL_TYPE_STRING && field->real_type() != MYSQL_TYPE_VARCHAR && - (field->type() != FIELD_TYPE_FLOAT || field->decimals() == 0)) + (field->type() != MYSQL_TYPE_FLOAT || field->decimals() == 0)) { return !store_val_in_field(field, right_item, CHECK_FIELD_WARN); } @@ -11784,7 +12067,7 @@ part_of_refkey(TABLE *table,Field *field) for (uint part=0 ; part < ref_parts ; part++,key_part++) if (field->eq(key_part->field) && - !(key_part->key_part_flag & HA_PART_KEY_SEG)) + !(key_part->key_part_flag & (HA_PART_KEY_SEG | HA_NULL_PART))) return table->reginfo.join_tab->ref.items[part]; } return (Item*) 0; @@ -11943,7 +12226,7 @@ test_if_subkey(ORDER *order, TABLE *table, uint ref, uint ref_key_parts, /* - Check if GROUP BY/DISTINCT can be optimized away because the set is + Check if GROUP BY/DISTINCT can be optimized away because the set is already known to be distinct. SYNOPSIS @@ -11951,7 +12234,7 @@ test_if_subkey(ORDER *order, TABLE *table, uint ref, uint ref_key_parts, table The table to operate on. find_func function to iterate over the list and search for a field - + DESCRIPTION Used in removing the GROUP BY/DISTINCT of the following types of statements: @@ -11962,12 +12245,13 @@ test_if_subkey(ORDER *order, TABLE *table, uint ref, uint ref_key_parts, then <any combination of a,b,c>,{whatever} is also distinct This function checks if all the key parts of any of the unique keys - of the table are referenced by a list : either the select list + of the table are referenced by a list : either the select list through find_field_in_item_list or GROUP BY list through find_field_in_order_list. - If the above holds then we can safely remove the GROUP BY/DISTINCT, + If the above holds and the key parts cannot contain NULLs then we + can safely remove the GROUP BY/DISTINCT, as no result set can be more distinct than an unique key. - + RETURN VALUE 1 found 0 not found. @@ -11990,7 +12274,8 @@ list_contains_unique_index(TABLE *table, key_part < key_part_end; key_part++) { - if (!find_func(key_part->field, data)) + if (key_part->field->maybe_null() || + !find_func(key_part->field, data)) break; } if (key_part == key_part_end) @@ -12098,13 +12383,14 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, DBUG_ENTER("test_if_skip_sort_order"); LINT_INIT(ref_key_parts); + /* Check which keys can be used to resolve ORDER BY. */ + usable_keys= table->keys_in_use_for_query; + /* - Check which keys can be used to resolve ORDER BY. - We must not try to use disabled keys. + Keys disabled by ALTER TABLE ... DISABLE KEYS should have already + been taken into account. */ - usable_keys= table->s->keys_in_use; - /* we must not consider keys that are disabled by IGNORE INDEX */ - usable_keys.intersect(table->keys_in_use_for_query); + DBUG_ASSERT(usable_keys.is_subset(table->s->keys_in_use)); for (ORDER *tmp_order=order; tmp_order ; tmp_order=tmp_order->next) { @@ -12352,7 +12638,7 @@ static int create_sort_index(THD *thd, JOIN *join, ORDER *order, ha_rows filesort_limit, ha_rows select_limit) { - uint length; + uint length= 0; ha_rows examined_rows; TABLE *table; SQL_SELECT *select; @@ -12373,8 +12659,10 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order, !(join->select_options & SELECT_BIG_RESULT)) && test_if_skip_sort_order(tab,order,select_limit,0)) DBUG_RETURN(0); + for (ORDER *ord= join->order; ord; ord= ord->next) + length++; if (!(join->sortorder= - make_unireg_sortorder(order,&length,join->sortorder))) + make_unireg_sortorder(order, &length, join->sortorder))) goto err; /* purecov: inspected */ table->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), @@ -12416,7 +12704,8 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order, /* Fill schema tables with data before filesort if it's necessary */ if ((join->select_lex->options & OPTION_SCHEMA_TABLE) && - get_schema_tables_result(join)) + !thd->lex->describe && + get_schema_tables_result(join, PROCESSED_BY_CREATE_SORT_INDEX)) goto err; if (table->s->tmp_table) @@ -12549,8 +12838,9 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having) DBUG_RETURN(0); } Field **first_field=entry->field+entry->s->fields - field_count; - offset= field_count ? - entry->field[entry->s->fields - field_count]->offset() : 0; + offset= (field_count ? + entry->field[entry->s->fields - field_count]-> + offset(entry->record[0]) : 0); reclength=entry->s->reclength-offset; free_io_cache(entry); // Safety @@ -12780,22 +13070,24 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length, for (ORDER *tmp = order; tmp; tmp=tmp->next) count++; if (!sortorder) - sortorder= (SORT_FIELD*) sql_alloc(sizeof(SORT_FIELD)*(count+1)); - pos=sort=sortorder; + sortorder= (SORT_FIELD*) sql_alloc(sizeof(SORT_FIELD) * + (max(count, *length) + 1)); + pos= sort= sortorder; + if (!pos) return 0; for (;order;order=order->next,pos++) { - pos->field=0; pos->item=0; - if (order->item[0]->type() == Item::FIELD_ITEM) - pos->field= ((Item_field*) (*order->item))->field; - else if (order->item[0]->type() == Item::SUM_FUNC_ITEM && - !order->item[0]->const_item()) - pos->field= ((Item_sum*) order->item[0])->get_tmp_table_field(); - else if (order->item[0]->type() == Item::COPY_STR_ITEM) + Item *item= order->item[0]->real_item(); + pos->field= 0; pos->item= 0; + if (item->type() == Item::FIELD_ITEM) + pos->field= ((Item_field*) item)->field; + else if (item->type() == Item::SUM_FUNC_ITEM && !item->const_item()) + pos->field= ((Item_sum*) item)->get_tmp_table_field(); + else if (item->type() == Item::COPY_STR_ITEM) { // Blob patch - pos->item= ((Item_copy_string*) (*order->item))->item; + pos->item= ((Item_copy_string*) item)->item; } else pos->item= *order->item; @@ -13135,7 +13427,7 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, Item **select_item; /* The corresponding item from the SELECT clause. */ Field *from_field; /* The corresponding field from the FROM clause. */ uint counter; - bool unaliased; + enum_resolution_type resolution; /* Local SP variables may be int but are expressions, not positions. @@ -13158,7 +13450,7 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, } /* Lookup the current GROUP/ORDER field in the SELECT clause. */ select_item= find_item_in_list(order_item, fields, &counter, - REPORT_EXCEPT_NOT_FOUND, &unaliased); + REPORT_EXCEPT_NOT_FOUND, &resolution); if (!select_item) return TRUE; /* The item is not unique, or some other error occured. */ @@ -13172,7 +13464,7 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, original field name, we should additionaly check if we have conflict for this name (in case if we would perform lookup in all tables). */ - if (unaliased && !order_item->fixed && + if (resolution == RESOLVED_BEHIND_ALIAS && !order_item->fixed && order_item->fix_fields(thd, order->item)) return TRUE; @@ -13242,16 +13534,11 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, We check order_item->fixed because Item_func_group_concat can put arguments for which fix_fields already was called. */ - thd->lex->current_select->is_item_list_lookup= 1; if (!order_item->fixed && (order_item->fix_fields(thd, order->item) || (order_item= *order->item)->check_cols(1) || thd->is_fatal_error)) - { - thd->lex->current_select->is_item_list_lookup= 0; return TRUE; /* Wrong field. */ - } - thd->lex->current_select->is_item_list_lookup= 0; uint el= all_fields.elements; all_fields.push_front(order_item); /* Add new field to field list. */ @@ -13309,49 +13596,83 @@ setup_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, bool *hidden_group_fields) { *hidden_group_fields=0; + ORDER *ord; + if (!order) return 0; /* Everything is ok */ - if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) - { - Item *item; - List_iterator<Item> li(fields); - while ((item=li++)) - item->marker=0; /* Marker that field is not used */ - } uint org_fields=all_fields.elements; thd->where="group statement"; - for (; order; order=order->next) + for (ord= order; ord; ord= ord->next) { - if (find_order_in_list(thd, ref_pointer_array, tables, order, fields, + if (find_order_in_list(thd, ref_pointer_array, tables, ord, fields, all_fields, TRUE)) return 1; - (*order->item)->marker=1; /* Mark found */ - if ((*order->item)->with_sum_func) + (*ord->item)->marker= UNDEF_POS; /* Mark found */ + if ((*ord->item)->with_sum_func) { - my_error(ER_WRONG_GROUP_FIELD, MYF(0), (*order->item)->full_name()); + my_error(ER_WRONG_GROUP_FIELD, MYF(0), (*ord->item)->full_name()); return 1; } } if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) { - /* Don't allow one to use fields that is not used in GROUP BY */ + /* + Don't allow one to use fields that is not used in GROUP BY + For each select a list of field references that aren't under an + aggregate function is created. Each field in this list keeps the + position of the select list expression which it belongs to. + + First we check an expression from the select list against the GROUP BY + list. If it's found there then it's ok. It's also ok if this expression + is a constant or an aggregate function. Otherwise we scan the list + of non-aggregated fields and if we'll find at least one field reference + that belongs to this expression and doesn't occur in the GROUP BY list + we throw an error. If there are no fields in the created list for a + select list expression this means that all fields in it are used under + aggregate functions. + */ Item *item; + Item_field *field; + int cur_pos_in_select_list= 0; List_iterator<Item> li(fields); + List_iterator<Item_field> naf_it(thd->lex->current_select->non_agg_fields); - while ((item=li++)) + field= naf_it++; + while (field && (item=li++)) { - if (item->type() != Item::SUM_FUNC_ITEM && !item->marker && - !item->const_item()) + if (item->type() != Item::SUM_FUNC_ITEM && item->marker >= 0 && + !item->const_item() && + !(item->real_item()->type() == Item::FIELD_ITEM && + item->used_tables() & OUTER_REF_TABLE_BIT)) { - /* - TODO: change ER_WRONG_FIELD_WITH_GROUP to more detailed - ER_NON_GROUPING_FIELD_USED - */ - my_error(ER_WRONG_FIELD_WITH_GROUP, MYF(0), item->full_name()); - return 1; + while (field) + { + /* Skip fields from previous expressions. */ + if (field->marker < cur_pos_in_select_list) + goto next_field; + /* Found a field from the next expression. */ + if (field->marker > cur_pos_in_select_list) + break; + /* + Check whether the field occur in the GROUP BY list. + Throw the error later if the field isn't found. + */ + for (ord= order; ord; ord= ord->next) + if ((*ord->item)->eq((Item*)field, 0)) + goto next_field; + /* + TODO: change ER_WRONG_FIELD_WITH_GROUP to more detailed + ER_NON_GROUPING_FIELD_USED + */ + my_error(ER_WRONG_FIELD_WITH_GROUP, MYF(0), field->full_name()); + return 1; +next_field: + field= naf_it++; + } } + cur_pos_in_select_list++; } } if (org_fields != all_fields.elements) @@ -13369,7 +13690,7 @@ setup_new_fields(THD *thd, List<Item> &fields, { Item **item; uint counter; - bool not_used; + enum_resolution_type not_used; DBUG_ENTER("setup_new_fields"); thd->mark_used_columns= MARK_COLUMNS_READ; // Not really needed, but... @@ -13477,10 +13798,10 @@ count_field_types(TMP_TABLE_PARAM *param, List<Item> &fields, param->quick_group=1; while ((field=li++)) { - Item::Type type=field->real_item()->type(); - if (type == Item::FIELD_ITEM) + Item::Type real_type= field->real_item()->type(); + if (real_type == Item::FIELD_ITEM) param->field_count++; - else if (type == Item::SUM_FUNC_ITEM) + else if (real_type == Item::SUM_FUNC_ITEM) { if (! field->const_item()) { @@ -13577,11 +13898,11 @@ calc_group_buffer(JOIN *join,ORDER *group) if (field) { enum_field_types type; - if ((type= field->type()) == FIELD_TYPE_BLOB) + if ((type= field->type()) == MYSQL_TYPE_BLOB) key_length+=MAX_BLOB_WIDTH; // Can't be used as a key else if (type == MYSQL_TYPE_VARCHAR || type == MYSQL_TYPE_VAR_STRING) key_length+= field->field_length + HA_KEY_BLOB_LENGTH; - else if (type == FIELD_TYPE_BIT) + else if (type == MYSQL_TYPE_BIT) { /* Bit is usually stored as a longlong key for group fields */ key_length+= 8; // Big enough @@ -13750,9 +14071,16 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, if (real_pos->type() == Item::FIELD_ITEM) { Item_field *item; - pos= real_pos; - if (!(item= new Item_field(thd, ((Item_field*) pos)))) + if (!(item= new Item_field(thd, ((Item_field*) real_pos)))) goto err; + if (pos->type() == Item::REF_ITEM) + { + /* preserve the names of the ref when dereferncing */ + Item_ref *ref= (Item_ref *) pos; + item->db_name= ref->db_name; + item->table_name= ref->table_name; + item->name= ref->name; + } pos= item; if (item->field->flags & BLOB_FLAG) { @@ -14611,7 +14939,7 @@ int JOIN::rollup_send_data(uint idx) 1 if write_data_failed() */ -int JOIN::rollup_write_data(uint idx, TABLE *table) +int JOIN::rollup_write_data(uint idx, TABLE *table_arg) { uint i; for (i= send_group_parts ; i-- > idx ; ) @@ -14622,7 +14950,7 @@ int JOIN::rollup_write_data(uint idx, TABLE *table) ref_pointer_array_size); if ((!having || having->val_int())) { - int error; + int write_error; Item *item; List_iterator_fast<Item> it(rollup.fields[i]); while ((item= it++)) @@ -14631,10 +14959,10 @@ int JOIN::rollup_write_data(uint idx, TABLE *table) item->save_in_result_field(1); } copy_sum_funcs(sum_funcs_end[i+1], sum_funcs_end[i]); - if ((error= table->file->write_row(table->record[0]))) + if ((write_error= table_arg->file->write_row(table_arg->record[0]))) { - if (create_myisam_from_heap(thd, table, &tmp_table_param, - error, 0)) + if (create_myisam_from_heap(thd, table_arg, &tmp_table_param, + write_error, 0)) return 1; } } @@ -14830,9 +15158,9 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, } else { - TABLE_LIST *tab=table->pos_in_table_list; - item_list.push_back(new Item_string(tab->alias, - strlen(tab->alias), + TABLE_LIST *real_table= table->pos_in_table_list; + item_list.push_back(new Item_string(real_table->alias, + strlen(real_table->alias), cs)); } /* "partitions" column */ @@ -14945,7 +15273,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, Item_float *filtered; float f; if (examined_rows) - f= 100.0 * join->best_positions[i].records_read / examined_rows; + f= (float) (100.0 * join->best_positions[i].records_read / + examined_rows); else f= 0.0; item_list.push_back((filtered= new Item_float(f))); @@ -14964,6 +15293,24 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, if (tab->info) item_list.push_back(new Item_string(tab->info,strlen(tab->info),cs)); + else if (tab->packed_info & TAB_INFO_HAVE_VALUE) + { + if (tab->packed_info & TAB_INFO_USING_INDEX) + extra.append(STRING_WITH_LEN("; Using index")); + if (tab->packed_info & TAB_INFO_USING_WHERE) + extra.append(STRING_WITH_LEN("; Using where")); + if (tab->packed_info & TAB_INFO_FULL_SCAN_ON_NULL) + extra.append(STRING_WITH_LEN("; Full scan on NULL key")); + /* Skip initial "; "*/ + const char *str= extra.ptr(); + uint32 len= extra.length(); + if (len) + { + str += 2; + len -= 2; + } + item_list.push_back(new Item_string(str, len, cs)); + } else { if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION || @@ -15022,6 +15369,15 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, } if (distinct & test_all_bits(used_tables,thd->used_tables)) extra.append(STRING_WITH_LEN("; Distinct")); + + for (uint part= 0; part < tab->ref.key_parts; part++) + { + if (tab->ref.cond_guards[part]) + { + extra.append(STRING_WITH_LEN("; Full scan on NULL key")); + break; + } + } /* Skip initial "; "*/ const char *str= extra.ptr(); @@ -15294,10 +15650,13 @@ void st_select_lex::print(THD *thd, String *str) Item *cur_where= where; if (join) cur_where= join->conds; - if (cur_where) + if (cur_where || cond_value != Item::COND_UNDEF) { str->append(STRING_WITH_LEN(" where ")); - cur_where->print(str); + if (cur_where) + cur_where->print(str); + else + str->append(cond_value != Item::COND_FALSE ? "1" : "0"); } // group by & olap @@ -15323,10 +15682,13 @@ void st_select_lex::print(THD *thd, String *str) if (join) cur_having= join->having; - if (cur_having) + if (cur_having || having_value != Item::COND_UNDEF) { str->append(STRING_WITH_LEN(" having ")); - cur_having->print(str); + if (cur_having) + cur_having->print(str); + else + str->append(having_value != Item::COND_FALSE ? "1" : "0"); } if (order_list.elements) diff --git a/sql/sql_select.h b/sql/sql_select.h index a66529a6459..2e01e54e4ca 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -36,6 +35,17 @@ typedef struct keyuse_t { satisfied if val has NULL 'value'. */ bool null_rejecting; + /* + !NULL - This KEYUSE was created from an equality that was wrapped into + an Item_func_trig_cond. This means the equality (and validity of + this KEYUSE element) can be turned on and off. The on/off state + is indicted by the pointed value: + *cond_guard == TRUE <=> equality condition is on + *cond_guard == FALSE <=> equality condition is off + + NULL - Otherwise (the source equality can't be turned off) + */ + bool *cond_guard; } KEYUSE; class store_key; @@ -50,6 +60,18 @@ typedef struct st_table_ref byte *key_buff2; // key_buff+key_length store_key **key_copy; // Item **items; // val()'s for each keypart + /* + Array of pointers to trigger variables. Some/all of the pointers may be + NULL. The ref access can be used iff + + for each used key part i, (!cond_guards[i] || *cond_guards[i]) + + This array is used by subquery code. The subquery code may inject + triggered conditions, i.e. conditions that can be 'switched off'. A ref + access created from such condition is not valid when at least one of the + underlying conditions is switched off (see subquery code for more details) + */ + bool **cond_guards; /* (null_rejecting & (1<<i)) means the condition is '=' and no matching rows will be produced if items[i] IS NULL (see add_not_null_conds()) @@ -98,6 +120,13 @@ enum enum_nested_loop_state NESTED_LOOP_QUERY_LIMIT= 3, NESTED_LOOP_CURSOR_LIMIT= 4 }; + +/* Values for JOIN_TAB::packed_info */ +#define TAB_INFO_HAVE_VALUE 1 +#define TAB_INFO_USING_INDEX 2 +#define TAB_INFO_USING_WHERE 4 +#define TAB_INFO_FULL_SCAN_ON_NULL 8 + typedef enum_nested_loop_state (*Next_select_func)(JOIN *, struct st_join_table *, bool); typedef int (*Read_record_func)(struct st_join_table *tab); @@ -119,7 +148,15 @@ typedef struct st_join_table { st_join_table *last_inner; /* last table table for embedding outer join */ st_join_table *first_upper; /* first inner table for embedding outer join */ st_join_table *first_unmatched; /* used for optimization purposes only */ + + /* Special content for EXPLAIN 'Extra' column or NULL if none */ const char *info; + /* + Bitmap of TAB_INFO_* bits that encodes special line for EXPLAIN 'Extra' + column, or 0 if there is no info. + */ + uint packed_info; + Read_record_func read_first_record; Next_select_func next_select; READ_RECORD read_record; @@ -294,7 +331,7 @@ public: bool need_tmp, hidden_group_fields; DYNAMIC_ARRAY keyuse; - Item::cond_result cond_value; + Item::cond_result cond_value, having_value; List<Item> all_fields; // to store all fields that used in query //Above list changed to use temporary table List<Item> tmp_all_fields1, tmp_all_fields2, tmp_all_fields3; @@ -424,7 +461,7 @@ public: Item_sum ***func); int rollup_send_data(uint idx); int rollup_write_data(uint idx, TABLE *table); - bool test_in_subselect(Item **where); + void remove_subq_pushed_predicates(Item **where); /* Release memory and, if possible, the open tables held by this execution plan (and nested plans). It's used to release some tables before @@ -494,12 +531,13 @@ class store_key :public Sql_alloc Field *to_field; // Store data here char *null_ptr; char err; - public: +public: + bool null_key; /* TRUE <=> the value of the key has a null part */ enum store_key_result { STORE_KEY_OK, STORE_KEY_FATAL, STORE_KEY_CONV }; store_key(THD *thd, Field *field_arg, char *ptr, char *null, uint length) - :null_ptr(null),err(0) + :null_ptr(null), err(0), null_key(0) { - if (field_arg->type() == FIELD_TYPE_BLOB) + if (field_arg->type() == MYSQL_TYPE_BLOB) { /* Key segments are always packed with a 2 byte length prefix */ to_field= new Field_varstring(ptr, length, 2, (uchar*) null, 1, @@ -540,6 +578,7 @@ class store_key_field: public store_key table->write_set); copy_field.do_copy(©_field); dbug_tmp_restore_column_map(table->write_set, old_map); + null_key= to_field->is_null(); return err != 0 ? STORE_KEY_FATAL : STORE_KEY_OK; } const char *name() const { return field_name; } @@ -564,8 +603,8 @@ public: table->write_set); int res= item->save_in_field(to_field, 1); dbug_tmp_restore_column_map(table->write_set, old_map); + null_key= to_field->is_null() || item->null_value; return (err != 0 || res > 2 ? STORE_KEY_FATAL : (store_key_result) res); - } const char *name() const { return "func"; } }; @@ -595,6 +634,7 @@ public: err= res; } } + null_key= to_field->is_null() || item->null_value; return (err > 2 ? STORE_KEY_FATAL : (store_key_result) err); } const char *name() const { return "const"; } diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc new file mode 100644 index 00000000000..9a5176d9fe3 --- /dev/null +++ b/sql/sql_servers.cc @@ -0,0 +1,1236 @@ +/* Copyright (C) 2000-2003 MySQL 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 + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +/* + The servers are saved in the system table "servers" +*/ + +#include "mysql_priv.h" +#include "hash_filo.h" +#include <m_ctype.h> +#include <stdarg.h> +#include "sp_head.h" +#include "sp.h" + +HASH servers_cache; +pthread_mutex_t servers_cache_mutex; // To init the hash +uint servers_cache_initialised=FALSE; +/* Version of server table. incremented by servers_load */ +static uint servers_version=0; +static MEM_ROOT mem; +static rw_lock_t THR_LOCK_servers; + +static byte *servers_cache_get_key(FOREIGN_SERVER *server, uint *length, + my_bool not_used __attribute__((unused))) +{ + DBUG_ENTER("servers_cache_get_key"); + DBUG_PRINT("info", ("server_name_length %d server_name %s", + server->server_name_length, + server->server_name)); + + *length= (uint) server->server_name_length; + DBUG_RETURN((byte*) server->server_name); +} + +/* + Initialize structures responsible for servers used in federated + server scheme information for them from the server + table in the 'mysql' database. + + SYNOPSIS + servers_init() + dont_read_server_table TRUE if we want to skip loading data from + server table and disable privilege checking. + + NOTES + This function is mostly responsible for preparatory steps, main work + on initialization and grants loading is done in servers_reload(). + + RETURN VALUES + 0 ok + 1 Could not initialize servers +*/ + +my_bool servers_init(bool dont_read_servers_table) +{ + THD *thd; + my_bool return_val= 0; + DBUG_ENTER("servers_init"); + + /* init the mutex */ + if (pthread_mutex_init(&servers_cache_mutex, MY_MUTEX_INIT_FAST)) + DBUG_RETURN(1); + + if (my_rwlock_init(&THR_LOCK_servers, NULL)) + DBUG_RETURN(1); + + /* initialise our servers cache */ + if (hash_init(&servers_cache, system_charset_info, 32, 0, 0, + (hash_get_key) servers_cache_get_key, 0, 0)) + { + return_val= 1; /* we failed, out of memory? */ + goto end; + } + + /* Initialize the mem root for data */ + init_alloc_root(&mem, ACL_ALLOC_BLOCK_SIZE, 0); + + /* + at this point, the cache is initialised, let it be known + */ + servers_cache_initialised= TRUE; + + if (dont_read_servers_table) + goto end; + + /* + To be able to run this from boot, we allocate a temporary THD + */ + if (!(thd=new THD)) + DBUG_RETURN(1); + thd->thread_stack= (char*) &thd; + thd->store_globals(); + /* + It is safe to call servers_reload() since servers_* arrays and hashes which + will be freed there are global static objects and thus are initialized + by zeros at startup. + */ + return_val= servers_reload(thd); + delete thd; + /* Remember that we don't have a THD */ + my_pthread_setspecific_ptr(THR_THD, 0); + +end: + DBUG_RETURN(return_val); +} + +/* + Initialize server structures + + SYNOPSIS + servers_load() + thd Current thread + tables List containing open "mysql.servers" + + RETURN VALUES + FALSE Success + TRUE Error +*/ + +static my_bool servers_load(THD *thd, TABLE_LIST *tables) +{ + TABLE *table; + READ_RECORD read_record_info; + my_bool return_val= TRUE; + DBUG_ENTER("servers_load"); + + if (!servers_cache_initialised) + DBUG_RETURN(0); + + /* need to figure out how to utilise this variable */ + servers_version++; /* servers updated */ + + /* first, send all cached rows to sleep with the fishes, oblivion! + I expect this crappy comment replaced */ + free_root(&mem, MYF(MY_MARK_BLOCKS_FREE)); + my_hash_reset(&servers_cache); + + init_read_record(&read_record_info,thd,table=tables[0].table,NULL,1,0); + while (!(read_record_info.read_record(&read_record_info))) + { + /* return_val is already TRUE, so no need to set */ + if ((get_server_from_table_to_cache(table))) + goto end; + } + + return_val=0; + +end: + end_read_record(&read_record_info); + DBUG_RETURN(return_val); +} + + +/* + Forget current servers cache and read new servers + from the conneciton table. + + SYNOPSIS + servers_reload() + thd Current thread + + NOTE + All tables of calling thread which were open and locked by LOCK TABLES + statement will be unlocked and closed. + This function is also used for initialization of structures responsible + for user/db-level privilege checking. + + RETURN VALUE + FALSE Success + TRUE Failure +*/ + +my_bool servers_reload(THD *thd) +{ + TABLE_LIST tables[1]; + my_bool return_val= 1; + DBUG_ENTER("servers_reload"); + + if (thd->locked_tables) + { // Can't have locked tables here + thd->lock=thd->locked_tables; + thd->locked_tables=0; + close_thread_tables(thd); + } + + /* + To avoid deadlocks we should obtain table locks before + obtaining servers_cache->lock mutex. + */ + bzero((char*) tables, sizeof(tables)); + tables[0].alias= tables[0].table_name= (char*) "servers"; + tables[0].db= (char*) "mysql"; + tables[0].lock_type= TL_READ; + + if (simple_open_n_lock_tables(thd, tables)) + { + sql_print_error("Fatal error: Can't open and lock privilege tables: %s", + thd->net.last_error); + goto end; + } + + DBUG_PRINT("info", ("locking servers_cache")); + VOID(pthread_mutex_lock(&servers_cache_mutex)); + + //old_servers_cache= servers_cache; + //old_mem=mem; + + if ((return_val= servers_load(thd, tables))) + { // Error. Revert to old list + /* blast, for now, we have no servers, discuss later way to preserve */ + + DBUG_PRINT("error",("Reverting to old privileges")); + servers_free(); + } + + DBUG_PRINT("info", ("unlocking servers_cache")); + VOID(pthread_mutex_unlock(&servers_cache_mutex)); + +end: + close_thread_tables(thd); + DBUG_RETURN(return_val); +} + +/* + Initialize structures responsible for servers used in federated + server scheme information for them from the server + table in the 'mysql' database. + + SYNOPSIS + get_server_from_table_to_cache() + TABLE *table open table pointer + + + NOTES + This function takes a TABLE pointer (pointing to an opened + table). With this open table, a FOREIGN_SERVER struct pointer + is allocated into root memory, then each member of the FOREIGN_SERVER + struct is populated. A char pointer takes the return value of get_field + for each column we're interested in obtaining, and if that pointer + isn't 0x0, the FOREIGN_SERVER member is set to that value, otherwise, + is set to the value of an empty string, since get_field would set it to + 0x0 if the column's value is empty, even if the default value for that + column is NOT NULL. + + RETURN VALUES + 0 ok + 1 could not insert server struct into global servers cache +*/ + +my_bool get_server_from_table_to_cache(TABLE *table) +{ + /* alloc a server struct */ + char *ptr; + char *blank= (char*)""; + FOREIGN_SERVER *server= (FOREIGN_SERVER *)alloc_root(&mem, + sizeof(FOREIGN_SERVER)); + DBUG_ENTER("get_server_from_table_to_cache"); + table->use_all_columns(); + + /* get each field into the server struct ptr */ + server->server_name= get_field(&mem, table->field[0]); + server->server_name_length= strlen(server->server_name); + ptr= get_field(&mem, table->field[1]); + server->host= ptr ? ptr : blank; + ptr= get_field(&mem, table->field[2]); + server->db= ptr ? ptr : blank; + ptr= get_field(&mem, table->field[3]); + server->username= ptr ? ptr : blank; + ptr= get_field(&mem, table->field[4]); + server->password= ptr ? ptr : blank; + ptr= get_field(&mem, table->field[5]); + server->sport= ptr ? ptr : blank; + + server->port= server->sport ? atoi(server->sport) : 0; + + ptr= get_field(&mem, table->field[6]); + server->socket= ptr && strlen(ptr) ? ptr : NULL; + ptr= get_field(&mem, table->field[7]); + server->scheme= ptr ? ptr : blank; + ptr= get_field(&mem, table->field[8]); + server->owner= ptr ? ptr : blank; + DBUG_PRINT("info", ("server->server_name %s", server->server_name)); + DBUG_PRINT("info", ("server->host %s", server->host)); + DBUG_PRINT("info", ("server->db %s", server->db)); + DBUG_PRINT("info", ("server->username %s", server->username)); + DBUG_PRINT("info", ("server->password %s", server->password)); + DBUG_PRINT("info", ("server->socket %s", server->socket)); + if (my_hash_insert(&servers_cache, (byte*) server)) + { + DBUG_PRINT("info", ("had a problem inserting server %s at %lx", + server->server_name, (long unsigned int) server)); + // error handling needed here + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); +} + +/* + SYNOPSIS + server_exists_in_table() + THD *thd - thread pointer + LEX_SERVER_OPTIONS *server_options - pointer to Lex->server_options + + NOTES + This function takes a LEX_SERVER_OPTIONS struct, which is very much the + same type of structure as a FOREIGN_SERVER, it contains the values parsed + in any one of the [CREATE|DELETE|DROP] SERVER statements. Using the + member "server_name", index_read_idx either founds the record and returns + 1, or doesn't find the record, and returns 0 + + RETURN VALUES + 0 record not found + 1 record found +*/ + +my_bool server_exists_in_table(THD *thd, LEX_SERVER_OPTIONS *server_options) +{ + int result= 1; + int error= 0; + TABLE_LIST tables; + TABLE *table; + DBUG_ENTER("server_exists"); + + bzero((char*) &tables, sizeof(tables)); + tables.db= (char*) "mysql"; + tables.alias= tables.table_name= (char*) "servers"; + + /* need to open before acquiring THR_LOCK_plugin or it will deadlock */ + if (! (table= open_ltable(thd, &tables, TL_WRITE))) + DBUG_RETURN(TRUE); + + table->use_all_columns(); + + rw_wrlock(&THR_LOCK_servers); + VOID(pthread_mutex_lock(&servers_cache_mutex)); + + /* set the field that's the PK to the value we're looking for */ + table->field[0]->store(server_options->server_name, + server_options->server_name_length, + system_charset_info); + + if ((error= table->file->index_read_idx(table->record[0], 0, + (byte *)table->field[0]->ptr, + table->key_info[0].key_length, + HA_READ_KEY_EXACT))) + { + if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) + { + table->file->print_error(error, MYF(0)); + result= -1; + } + result= 0; + DBUG_PRINT("info",("record for server '%s' not found!", + server_options->server_name)); + } + + VOID(pthread_mutex_unlock(&servers_cache_mutex)); + rw_unlock(&THR_LOCK_servers); + DBUG_RETURN(result); +} + +/* + SYNOPSIS + insert_server() + THD *thd - thread pointer + FOREIGN_SERVER *server - pointer to prepared FOREIGN_SERVER struct + + NOTES + This function takes a server object that is has all members properly + prepared, ready to be inserted both into the mysql.servers table and + the servers cache. + + RETURN VALUES + 0 - no error + other - error code +*/ + +int insert_server(THD *thd, FOREIGN_SERVER *server) +{ + int error= 0; + TABLE_LIST tables; + TABLE *table; + + DBUG_ENTER("insert_server"); + + bzero((char*) &tables, sizeof(tables)); + tables.db= (char*) "mysql"; + tables.alias= tables.table_name= (char*) "servers"; + + /* need to open before acquiring THR_LOCK_plugin or it will deadlock */ + if (! (table= open_ltable(thd, &tables, TL_WRITE))) + DBUG_RETURN(TRUE); + + /* lock mutex to make sure no changes happen */ + VOID(pthread_mutex_lock(&servers_cache_mutex)); + + /* lock table */ + rw_wrlock(&THR_LOCK_servers); + + /* insert the server into the table */ + if ((error= insert_server_record(table, server))) + goto end; + + /* insert the server into the cache */ + if ((error= insert_server_record_into_cache(server))) + goto end; + +end: + /* unlock the table */ + rw_unlock(&THR_LOCK_servers); + VOID(pthread_mutex_unlock(&servers_cache_mutex)); + DBUG_RETURN(error); +} + +/* + SYNOPSIS + int insert_server_record_into_cache() + FOREIGN_SERVER *server + + NOTES + This function takes a FOREIGN_SERVER pointer to an allocated (root mem) + and inserts it into the global servers cache + + RETURN VALUE + 0 - no error + >0 - error code + +*/ + +int insert_server_record_into_cache(FOREIGN_SERVER *server) +{ + int error=0; + DBUG_ENTER("insert_server_record_into_cache"); + /* + We succeded in insertion of the server to the table, now insert + the server to the cache + */ + DBUG_PRINT("info", ("inserting server %s at %lx, length %d", + server->server_name, (long unsigned int) server, + server->server_name_length)); + if (my_hash_insert(&servers_cache, (byte*) server)) + { + DBUG_PRINT("info", ("had a problem inserting server %s at %lx", + server->server_name, (long unsigned int) server)); + // error handling needed here + error= 1; + } + DBUG_RETURN(error); +} + +/* + SYNOPSIS + store_server_fields() + TABLE *table + FOREIGN_SERVER *server + + NOTES + This function takes an opened table object, and a pointer to an + allocated FOREIGN_SERVER struct, and then stores each member of + the FOREIGN_SERVER to the appropriate fields in the table, in + advance of insertion into the mysql.servers table + + RETURN VALUE + VOID + +*/ + +void store_server_fields(TABLE *table, FOREIGN_SERVER *server) +{ + + table->use_all_columns(); + /* + "server" has already been prepped by prepare_server_struct_for_<> + so, all we need to do is check if the value is set (> -1 for port) + + If this happens to be an update, only the server members that + have changed will be set. If an insert, then all will be set, + even if with empty strings + */ + if (server->host) + table->field[1]->store(server->host, + (uint) strlen(server->host), system_charset_info); + if (server->db) + table->field[2]->store(server->db, + (uint) strlen(server->db), system_charset_info); + if (server->username) + table->field[3]->store(server->username, + (uint) strlen(server->username), system_charset_info); + if (server->password) + table->field[4]->store(server->password, + (uint) strlen(server->password), system_charset_info); + if (server->port > -1) + table->field[5]->store(server->port); + + if (server->socket) + table->field[6]->store(server->socket, + (uint) strlen(server->socket), system_charset_info); + if (server->scheme) + table->field[7]->store(server->scheme, + (uint) strlen(server->scheme), system_charset_info); + if (server->owner) + table->field[8]->store(server->owner, + (uint) strlen(server->owner), system_charset_info); +} + +/* + SYNOPSIS + insert_server_record() + TABLE *table + FOREIGN_SERVER *server + + NOTES + This function takes the arguments of an open table object and a pointer + to an allocated FOREIGN_SERVER struct. It stores the server_name into + the first field of the table (the primary key, server_name column). With + this, index_read_idx is called, if the record is found, an error is set + to ER_FOREIGN_SERVER_EXISTS (the server with that server name exists in the + table), if not, then store_server_fields stores all fields of the + FOREIGN_SERVER to the table, then ha_write_row is inserted. If an error + is encountered in either index_read_idx or ha_write_row, then that error + is returned + + RETURN VALUE + 0 - no errors + >0 - error code + + */ + +int insert_server_record(TABLE *table, FOREIGN_SERVER *server) +{ + int error; + DBUG_ENTER("insert_server_record"); + table->use_all_columns(); + + empty_record(table); + + /* set the field that's the PK to the value we're looking for */ + table->field[0]->store(server->server_name, + server->server_name_length, + system_charset_info); + + /* read index until record is that specified in server_name */ + if ((error= table->file->index_read_idx(table->record[0], 0, + (byte *)table->field[0]->ptr, + table->key_info[0].key_length, + HA_READ_KEY_EXACT))) + { + /* if not found, err */ + if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) + { + table->file->print_error(error, MYF(0)); + error= 1; + } + /* store each field to be inserted */ + store_server_fields(table, server); + + DBUG_PRINT("info",("record for server '%s' not found!", + server->server_name)); + /* write/insert the new server */ + if ((error=table->file->ha_write_row(table->record[0]))) + { + table->file->print_error(error, MYF(0)); + } + else + error= 0; + } + else + error= ER_FOREIGN_SERVER_EXISTS; + DBUG_RETURN(error); +} + +/* + SYNOPSIS + drop_server() + THD *thd + LEX_SERVER_OPTIONS *server_options + + NOTES + This function takes as its arguments a THD object pointer and a pointer + to a LEX_SERVER_OPTIONS struct from the parser. The member 'server_name' + of this LEX_SERVER_OPTIONS struct contains the value of the server to be + deleted. The mysql.servers table is opened via open_ltable, a table object + returned, the servers cache mutex locked, then delete_server_record is + called with this table object and LEX_SERVER_OPTIONS server_name and + server_name_length passed, containing the name of the server to be + dropped/deleted, then delete_server_record_in_cache is called to delete + the server from the servers cache. + + RETURN VALUE + 0 - no error + > 0 - error code +*/ + +int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options) +{ + int error= 0; + TABLE_LIST tables; + TABLE *table; + + DBUG_ENTER("drop_server"); + DBUG_PRINT("info", ("server name server->server_name %s", + server_options->server_name)); + + bzero((char*) &tables, sizeof(tables)); + tables.db= (char*) "mysql"; + tables.alias= tables.table_name= (char*) "servers"; + + /* need to open before acquiring THR_LOCK_plugin or it will deadlock */ + if (! (table= open_ltable(thd, &tables, TL_WRITE))) + DBUG_RETURN(TRUE); + + rw_wrlock(&THR_LOCK_servers); + VOID(pthread_mutex_lock(&servers_cache_mutex)); + + + if ((error= delete_server_record(table, + server_options->server_name, + server_options->server_name_length))) + goto end; + + + if ((error= delete_server_record_in_cache(server_options))) + goto end; + +end: + VOID(pthread_mutex_unlock(&servers_cache_mutex)); + rw_unlock(&THR_LOCK_servers); + DBUG_RETURN(error); +} +/* + + SYNOPSIS + delete_server_record_in_cache() + LEX_SERVER_OPTIONS *server_options + + NOTES + This function's argument is a LEX_SERVER_OPTIONS struct pointer. This + function uses the "server_name" and "server_name_length" members of the + lex->server_options to search for the server in the servers_cache. Upon + returned the server (pointer to a FOREIGN_SERVER struct), it then deletes + that server from the servers_cache hash. + + RETURN VALUE + 0 - no error + +*/ + +int delete_server_record_in_cache(LEX_SERVER_OPTIONS *server_options) +{ + + int error= 0; + FOREIGN_SERVER *server; + DBUG_ENTER("delete_server_record_in_cache"); + + DBUG_PRINT("info",("trying to obtain server name %s length %d", + server_options->server_name, + server_options->server_name_length)); + + + if (!(server= (FOREIGN_SERVER *) hash_search(&servers_cache, + (byte*) server_options->server_name, + server_options->server_name_length))) + { + DBUG_PRINT("info", ("server_name %s length %d not found!", + server_options->server_name, + server_options->server_name_length)); + // what should be done if not found in the cache? + } + /* + We succeded in deletion of the server to the table, now delete + the server from the cache + */ + DBUG_PRINT("info",("deleting server %s length %d", + server->server_name, + server->server_name_length)); + + if (server) + VOID(hash_delete(&servers_cache, (byte*) server)); + + servers_version++; /* servers updated */ + + DBUG_RETURN(error); +} + +/* + + SYNOPSIS + update_server() + THD *thd + FOREIGN_SERVER *existing + FOREIGN_SERVER *altered + + NOTES + This function takes as arguments a THD object pointer, and two pointers, + one pointing to the existing FOREIGN_SERVER struct "existing" (which is + the current record as it is) and another pointer pointing to the + FOREIGN_SERVER struct with the members containing the modified/altered + values that need to be updated in both the mysql.servers table and the + servers_cache. It opens a table, passes the table and the altered + FOREIGN_SERVER pointer, which will be used to update the mysql.servers + table for the particular server via the call to update_server_record, + and in the servers_cache via update_server_record_in_cache. + + RETURN VALUE + 0 - no error + >0 - error code + +*/ + +int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered) +{ + int error= 0; + TABLE *table; + TABLE_LIST tables; + DBUG_ENTER("update_server"); + + bzero((char*) &tables, sizeof(tables)); + tables.db= (char*)"mysql"; + tables.alias= tables.table_name= (char*)"servers"; + + if (!(table= open_ltable(thd, &tables, TL_WRITE))) + DBUG_RETURN(1); + + rw_wrlock(&THR_LOCK_servers); + if ((error= update_server_record(table, altered))) + goto end; + + update_server_record_in_cache(existing, altered); + +end: + rw_unlock(&THR_LOCK_servers); + DBUG_RETURN(error); +} + +/* + + SYNOPSIS + update_server_record_in_cache() + FOREIGN_SERVER *existing + FOREIGN_SERVER *altered + + NOTES + This function takes as an argument the FOREIGN_SERVER structi pointer + for the existing server and the FOREIGN_SERVER struct populated with only + the members which have been updated. It then "merges" the "altered" struct + members to the existing server, the existing server then represents an + updated server. Then, the existing record is deleted from the servers_cache + HASH, then the updated record inserted, in essence replacing the old + record. + + RETURN VALUE + 0 - no error + 1 - error + +*/ + +int update_server_record_in_cache(FOREIGN_SERVER *existing, + FOREIGN_SERVER *altered) +{ + int error= 0; + DBUG_ENTER("update_server_record_in_cache"); + + /* + update the members that haven't been change in the altered server struct + with the values of the existing server struct + */ + merge_server_struct(existing, altered); + + /* + delete the existing server struct from the server cache + */ + VOID(hash_delete(&servers_cache, (byte*)existing)); + + /* + Insert the altered server struct into the server cache + */ + if (my_hash_insert(&servers_cache, (byte*)altered)) + { + DBUG_PRINT("info", ("had a problem inserting server %s at %lx", + altered->server_name, (long unsigned int) altered)); + error= 1; + } + + servers_version++; /* servers updated */ + DBUG_RETURN(error); +} + +/* + + SYNOPSIS + merge_server_struct() + FOREIGN_SERVER *from + FOREIGN_SERVER *to + + NOTES + This function takes as its arguments two pointers each to an allocated + FOREIGN_SERVER struct. The first FOREIGN_SERVER struct represents the struct + that we will obtain values from (hence the name "from"), the second + FOREIGN_SERVER struct represents which FOREIGN_SERVER struct we will be + "copying" any members that have a value to (hence the name "to") + + RETURN VALUE + VOID + +*/ + +void merge_server_struct(FOREIGN_SERVER *from, FOREIGN_SERVER *to) +{ + DBUG_ENTER("merge_server_struct"); + if (!to->host) + to->host= strdup_root(&mem, from->host); + if (!to->db) + to->db= strdup_root(&mem, from->db); + if (!to->username) + to->username= strdup_root(&mem, from->username); + if (!to->password) + to->password= strdup_root(&mem, from->password); + if (to->port == -1) + to->port= from->port; + if (!to->socket) + to->socket= strdup_root(&mem, from->socket); + if (!to->scheme) + to->scheme= strdup_root(&mem, from->scheme); + if (!to->owner) + to->owner= strdup_root(&mem, from->owner); + + DBUG_VOID_RETURN; +} + +/* + + SYNOPSIS + update_server_record() + TABLE *table + FOREIGN_SERVER *server + + NOTES + This function takes as its arguments an open TABLE pointer, and a pointer + to an allocated FOREIGN_SERVER structure representing an updated record + which needs to be inserted. The primary key, server_name is stored to field + 0, then index_read_idx is called to read the index to that record, the + record then being ready to be updated, if found. If not found an error is + set and error message printed. If the record is found, store_record is + called, then store_server_fields stores each field from the the members of + the updated FOREIGN_SERVER struct. + + RETURN VALUE + 0 - no error + +*/ + +int update_server_record(TABLE *table, FOREIGN_SERVER *server) +{ + int error=0; + DBUG_ENTER("update_server_record"); + table->use_all_columns(); + /* set the field that's the PK to the value we're looking for */ + table->field[0]->store(server->server_name, + server->server_name_length, + system_charset_info); + + if ((error= table->file->index_read_idx(table->record[0], 0, + (byte *)table->field[0]->ptr, + table->key_info[0].key_length, + HA_READ_KEY_EXACT))) + { + if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) + { + table->file->print_error(error, MYF(0)); + error= 1; + } + DBUG_PRINT("info",("server not found!")); + error= ER_FOREIGN_SERVER_DOESNT_EXIST; + } + else + { + /* ok, so we can update since the record exists in the table */ + store_record(table,record[1]); + store_server_fields(table, server); + if ((error=table->file->ha_update_row(table->record[1],table->record[0]))) + { + DBUG_PRINT("info",("problems with ha_update_row %d", error)); + goto end; + } + } + +end: + DBUG_RETURN(error); +} + +/* + + SYNOPSIS + delete_server_record() + TABLE *table + char *server_name + int server_name_length + + NOTES + + RETURN VALUE + 0 - no error + +*/ + +int delete_server_record(TABLE *table, + char *server_name, + int server_name_length) +{ + int error= 0; + DBUG_ENTER("delete_server_record"); + table->use_all_columns(); + + /* set the field that's the PK to the value we're looking for */ + table->field[0]->store(server_name, server_name_length, system_charset_info); + + if ((error= table->file->index_read_idx(table->record[0], 0, + (byte *)table->field[0]->ptr, + table->key_info[0].key_length, + HA_READ_KEY_EXACT))) + { + if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) + { + table->file->print_error(error, MYF(0)); + error= 1; + } + DBUG_PRINT("info",("server not found!")); + error= ER_FOREIGN_SERVER_DOESNT_EXIST; + } + else + { + if ((error= table->file->ha_delete_row(table->record[0]))) + table->file->print_error(error, MYF(0)); + } + + DBUG_RETURN(error); +} + +/* + + SYNOPSIS + create_server() + THD *thd + LEX_SERVER_OPTIONS *server_options + + NOTES + + RETURN VALUE + 0 - no error + +*/ + +int create_server(THD *thd, LEX_SERVER_OPTIONS *server_options) +{ + int error; + FOREIGN_SERVER *server; + + DBUG_ENTER("create_server"); + DBUG_PRINT("info", ("server_options->server_name %s", + server_options->server_name)); + + server= (FOREIGN_SERVER *)alloc_root(&mem, + sizeof(FOREIGN_SERVER)); + + if ((error= prepare_server_struct_for_insert(server_options, server))) + goto end; + + if ((error= insert_server(thd, server))) + goto end; + + DBUG_PRINT("info", ("error returned %d", error)); + +end: + DBUG_RETURN(error); +} + +/* + + SYNOPSIS + alter_server() + THD *thd + LEX_SERVER_OPTIONS *server_options + + NOTES + + RETURN VALUE + 0 - no error + +*/ + +int alter_server(THD *thd, LEX_SERVER_OPTIONS *server_options) +{ + int error= 0; + FOREIGN_SERVER *altered, *existing; + DBUG_ENTER("alter_server"); + DBUG_PRINT("info", ("server_options->server_name %s", + server_options->server_name)); + + altered= (FOREIGN_SERVER *)alloc_root(&mem, + sizeof(FOREIGN_SERVER)); + + VOID(pthread_mutex_lock(&servers_cache_mutex)); + + if (!(existing= (FOREIGN_SERVER *) hash_search(&servers_cache, + (byte*) server_options->server_name, + server_options->server_name_length))) + { + error= ER_FOREIGN_SERVER_DOESNT_EXIST; + goto end; + } + + if ((error= prepare_server_struct_for_update(server_options, existing, altered))) + goto end; + + if ((error= update_server(thd, existing, altered))) + goto end; + +end: + DBUG_PRINT("info", ("error returned %d", error)); + VOID(pthread_mutex_unlock(&servers_cache_mutex)); + DBUG_RETURN(error); +} + +/* + + SYNOPSIS + prepare_server_struct_for_insert() + LEX_SERVER_OPTIONS *server_options + FOREIGN_SERVER *server + + NOTES + + RETURN VALUE + 0 - no error + +*/ + +int prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options, + FOREIGN_SERVER *server) +{ + int error; + char *unset_ptr= (char*)""; + DBUG_ENTER("prepare_server_struct"); + + error= 0; + + /* these two MUST be set */ + server->server_name= strdup_root(&mem, server_options->server_name); + server->server_name_length= server_options->server_name_length; + + server->host= server_options->host ? + strdup_root(&mem, server_options->host) : unset_ptr; + + server->db= server_options->db ? + strdup_root(&mem, server_options->db) : unset_ptr; + + server->username= server_options->username ? + strdup_root(&mem, server_options->username) : unset_ptr; + + server->password= server_options->password ? + strdup_root(&mem, server_options->password) : unset_ptr; + + /* set to 0 if not specified */ + server->port= server_options->port > -1 ? + server_options->port : 0; + + server->socket= server_options->socket ? + strdup_root(&mem, server_options->socket) : unset_ptr; + + server->scheme= server_options->scheme ? + strdup_root(&mem, server_options->scheme) : unset_ptr; + + server->owner= server_options->owner ? + strdup_root(&mem, server_options->owner) : unset_ptr; + + DBUG_RETURN(error); +} + +/* + + SYNOPSIS + prepare_server_struct_for_update() + LEX_SERVER_OPTIONS *server_options + + NOTES + + RETURN VALUE + 0 - no error + +*/ + +int prepare_server_struct_for_update(LEX_SERVER_OPTIONS *server_options, + FOREIGN_SERVER *existing, + FOREIGN_SERVER *altered) +{ + int error; + DBUG_ENTER("prepare_server_struct_for_update"); + error= 0; + + altered->server_name= strdup_root(&mem, server_options->server_name); + altered->server_name_length= server_options->server_name_length; + DBUG_PRINT("info", ("existing name %s altered name %s", + existing->server_name, altered->server_name)); + + /* + The logic here is this: is this value set AND is it different + than the existing value? + */ + altered->host= + (server_options->host && (strcmp(server_options->host, existing->host))) ? + strdup_root(&mem, server_options->host) : 0; + + altered->db= + (server_options->db && (strcmp(server_options->db, existing->db))) ? + strdup_root(&mem, server_options->db) : 0; + + altered->username= + (server_options->username && + (strcmp(server_options->username, existing->username))) ? + strdup_root(&mem, server_options->username) : 0; + + altered->password= + (server_options->password && + (strcmp(server_options->password, existing->password))) ? + strdup_root(&mem, server_options->password) : 0; + + /* + port is initialised to -1, so if unset, it will be -1 + */ + altered->port= (server_options->port > -1 && + server_options->port != existing->port) ? + server_options->port : -1; + + altered->socket= + (server_options->socket && + (strcmp(server_options->socket, existing->socket))) ? + strdup_root(&mem, server_options->socket) : 0; + + altered->scheme= + (server_options->scheme && + (strcmp(server_options->scheme, existing->scheme))) ? + strdup_root(&mem, server_options->scheme) : 0; + + altered->owner= + (server_options->owner && + (strcmp(server_options->owner, existing->owner))) ? + strdup_root(&mem, server_options->owner) : 0; + + DBUG_RETURN(error); +} + +/* + + SYNOPSIS + servers_free() + bool end + + NOTES + + RETURN VALUE + void + +*/ + +void servers_free(bool end) +{ + DBUG_ENTER("servers_free"); + if (!servers_cache_initialised) + DBUG_VOID_RETURN; + VOID(pthread_mutex_destroy(&servers_cache_mutex)); + servers_cache_initialised=0; + free_root(&mem,MYF(0)); + hash_free(&servers_cache); + DBUG_VOID_RETURN; +} + + + +/* + + SYNOPSIS + get_server_by_name() + const char *server_name + + NOTES + + RETURN VALUE + FOREIGN_SERVER * + +*/ + +FOREIGN_SERVER *get_server_by_name(const char *server_name) +{ + ulong error_num=0; + uint server_name_length; + FOREIGN_SERVER *server= 0; + DBUG_ENTER("get_server_by_name"); + DBUG_PRINT("info", ("server_name %s", server_name)); + + server_name_length= strlen(server_name); + + if (! server_name || !strlen(server_name)) + { + DBUG_PRINT("info", ("server_name not defined!")); + error_num= 1; + DBUG_RETURN((FOREIGN_SERVER *)NULL); + } + + DBUG_PRINT("info", ("locking servers_cache")); + VOID(pthread_mutex_lock(&servers_cache_mutex)); + if (!(server= (FOREIGN_SERVER *) hash_search(&servers_cache, + (byte*) server_name, + server_name_length))) + { + DBUG_PRINT("info", ("server_name %s length %d not found!", + server_name, server_name_length)); + server= (FOREIGN_SERVER *) NULL; + } + DBUG_PRINT("info", ("unlocking servers_cache")); + VOID(pthread_mutex_unlock(&servers_cache_mutex)); + DBUG_RETURN(server); + +} diff --git a/sql/sql_servers.h b/sql/sql_servers.h new file mode 100644 index 00000000000..23b8cefd5bb --- /dev/null +++ b/sql/sql_servers.h @@ -0,0 +1,64 @@ +/* Copyright (C) 2006 MySQL 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 + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "slave.h" // for tables_ok(), rpl_filter + +/* structs */ +typedef struct st_federated_server +{ + char *server_name; + long port; + uint server_name_length; + char *db, *scheme, *username, *password, *socket, *owner, *host, *sport; +} FOREIGN_SERVER; + +/* cache handlers */ +my_bool servers_init(bool dont_read_server_table); +my_bool servers_reload(THD *thd); +my_bool get_server_from_table_to_cache(TABLE *table); +void servers_free(bool end=0); + +/* insert functions */ +int create_server(THD *thd, LEX_SERVER_OPTIONS *server_options); +int insert_server(THD *thd, FOREIGN_SERVER *server_options); +int insert_server_record(TABLE *table, FOREIGN_SERVER *server); +int insert_server_record_into_cache(FOREIGN_SERVER *server); +void store_server_fields_for_insert(TABLE *table, FOREIGN_SERVER *server); +void store_server_fields_for_insert(TABLE *table, + FOREIGN_SERVER *existing, + FOREIGN_SERVER *altered); +int prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options, + FOREIGN_SERVER *server); + +/* drop functions */ +int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options); +int delete_server_record(TABLE *table, + char *server_name, + int server_name_length); +int delete_server_record_in_cache(LEX_SERVER_OPTIONS *server_options); + +/* update functions */ +int alter_server(THD *thd, LEX_SERVER_OPTIONS *server_options); +int prepare_server_struct_for_update(LEX_SERVER_OPTIONS *server_options, + FOREIGN_SERVER *existing, + FOREIGN_SERVER *altered); +int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered); +int update_server_record(TABLE *table, FOREIGN_SERVER *server); +int update_server_record_in_cache(FOREIGN_SERVER *existing, + FOREIGN_SERVER *altered); +/* utility functions */ +void merge_server_struct(FOREIGN_SERVER *from, FOREIGN_SERVER *to); +FOREIGN_SERVER *get_server_by_name(const char *server_name); +my_bool server_exists_in_table(THD *thd, char *server_name); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 30bbc40f21b..a91eb28b191 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +32,6 @@ #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" #endif - enum enum_i_s_events_fields { ISE_EVENT_CATALOG= 0, @@ -58,11 +56,11 @@ enum enum_i_s_events_fields }; +#ifndef NO_EMBEDDED_ACCESS_CHECKS static const char *grant_names[]={ "select","insert","update","delete","create","drop","reload","shutdown", "process","file","grant","references","index","alter"}; -#ifndef NO_EMBEDDED_ACCESS_CHECKS static TYPELIB grant_types = { sizeof(grant_names)/sizeof(char **), "grant_types", grant_names, NULL}; @@ -141,7 +139,6 @@ static my_bool show_plugins(THD *thd, st_plugin_int *plugin, { TABLE *table= (TABLE*) arg; struct st_mysql_plugin *plug= plugin->plugin; - Protocol *protocol= thd->protocol; CHARSET_INFO *cs= system_charset_info; char version_buf[20]; @@ -154,8 +151,7 @@ static my_bool show_plugins(THD *thd, st_plugin_int *plugin, cs); - switch (plugin->state) - { + switch (plugin->state) { /* case PLUGIN_IS_FREED: does not happen */ case PLUGIN_IS_DELETED: table->field[2]->store(STRING_WITH_LEN("DELETED"), cs); @@ -491,13 +487,7 @@ bool mysqld_show_column_types(THD *thd) FIND_FILES_DIR no such directory, or directory can't be read */ -enum find_files_result { - FIND_FILES_OK, - FIND_FILES_OOM, - FIND_FILES_DIR -}; -static find_files_result find_files(THD *thd, List<char> *files, const char *db, const char *path, const char *wild, bool dir) @@ -695,11 +685,10 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) bool mysqld_show_create_db(THD *thd, char *dbname, HA_CREATE_INFO *create_info) { - Security_context *sctx= thd->security_ctx; - int length; char buff[2048]; String buffer(buff, sizeof(buff), system_charset_info); #ifndef NO_EMBEDDED_ACCESS_CHECKS + Security_context *sctx= thd->security_ctx; uint db_access; #endif HA_CREATE_INFO create; @@ -1028,7 +1017,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, HA_CREATE_INFO *create_info_arg) { List<Item> field_list; - char tmp[MAX_FIELD_WIDTH], *for_str, buff[128], *end, uname[NAME_LEN*3+1]; + char tmp[MAX_FIELD_WIDTH], *for_str, buff[128]; const char *alias; String type(tmp, sizeof(tmp), system_charset_info); Field **ptr,*field; @@ -1123,7 +1112,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, if (flags & NOT_NULL_FLAG) packet->append(STRING_WITH_LEN(" NOT NULL")); - else if (field->type() == FIELD_TYPE_TIMESTAMP) + else if (field->type() == MYSQL_TYPE_TIMESTAMP) { /* TIMESTAMP field require explicit NULL flag, because unlike @@ -1139,7 +1128,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, has_now_default= table->timestamp_field == field && field->unireg_check != Field::TIMESTAMP_UN_FIELD; - has_default= (field->type() != FIELD_TYPE_BLOB && + has_default= (field->type() != MYSQL_TYPE_BLOB && !(field->flags & NO_DEFAULT_VALUE_FLAG) && field->unireg_check != Field::NEXT_NUMBER && !((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) @@ -1274,7 +1263,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, to the CREATE TABLE statement */ - if ((for_str= file->get_tablespace_name(thd))) + if ((for_str= file->get_tablespace_name(thd,0,0))) { packet->append(STRING_WITH_LEN(" /*!50100 TABLESPACE ")); packet->append(for_str, strlen(for_str)); @@ -1315,8 +1304,9 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, but may extrapolate its existence from that of an AUTO_INCREMENT column. */ - if(create_info.auto_increment_value > 1) + if (create_info.auto_increment_value > 1) { + char *end; packet->append(STRING_WITH_LEN(" AUTO_INCREMENT=")); end= longlong10_to_str(create_info.auto_increment_value, buff,10); packet->append(buff, (uint) (end - buff)); @@ -1346,6 +1336,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, if (share->min_rows) { + char *end; packet->append(STRING_WITH_LEN(" MIN_ROWS=")); end= longlong10_to_str(share->min_rows, buff, 10); packet->append(buff, (uint) (end- buff)); @@ -1353,6 +1344,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, if (share->max_rows && !table_list->schema_table) { + char *end; packet->append(STRING_WITH_LEN(" MAX_ROWS=")); end= longlong10_to_str(share->max_rows, buff, 10); packet->append(buff, (uint) (end - buff)); @@ -1360,6 +1352,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, if (share->avg_row_length) { + char *end; packet->append(STRING_WITH_LEN(" AVG_ROW_LENGTH=")); end= longlong10_to_str(share->avg_row_length, buff,10); packet->append(buff, (uint) (end - buff)); @@ -1380,6 +1373,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, } if (table->s->key_block_size) { + char *end; packet->append(STRING_WITH_LEN(" KEY_BLOCK_SIZE=")); end= longlong10_to_str(table->s->key_block_size, buff, 10); packet->append(buff, (uint) (end - buff)); @@ -1632,7 +1626,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) field_list.push_back(field=new Item_empty_string("db",NAME_LEN)); field->maybe_null=1; field_list.push_back(new Item_empty_string("Command",16)); - field_list.push_back(new Item_return_int("Time",7, FIELD_TYPE_LONG)); + field_list.push_back(new Item_return_int("Time",7, MYSQL_TYPE_LONG)); field_list.push_back(field=new Item_empty_string("State",30)); field->maybe_null=1; field_list.push_back(field=new Item_empty_string("Info",max_query_length)); @@ -1667,7 +1661,9 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) "%s:%u", tmp_sctx->host_or_ip, tmp->peer_port); } else - thd_info->host= thd->strdup(tmp_sctx->host_or_ip); + thd_info->host= thd->strdup(tmp_sctx->host_or_ip[0] ? + tmp_sctx->host_or_ip : + tmp_sctx->host ? tmp_sctx->host : ""); if ((thd_info->db=tmp->db)) // Safe test thd_info->db=thd->strdup(thd_info->db); thd_info->command=(int) tmp->command; @@ -1691,10 +1687,6 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) if (mysys_var) pthread_mutex_unlock(&mysys_var->mutex); -#if !defined(DONT_USE_THR_ALARM) && ! defined(SCO) - if (pthread_kill(tmp->real_id,0)) - tmp->proc_info="*** DEAD ***"; // This shouldn't happen -#endif #ifdef EXTRA_DEBUG thd_info->start_time= tmp->time_after_lock; #else @@ -2073,7 +2065,7 @@ static bool show_status_array(THD *thd, const char *wild, if (show_type == SHOW_SYS) { - show_type= ((sys_var*) value)->type(); + show_type= ((sys_var*) value)->show_type(); value= (char*) ((sys_var*) value)->value_ptr(thd, value_type, &null_lex_str); } @@ -2126,7 +2118,7 @@ static bool show_status_array(THD *thd, const char *wild, end= strend(pos); break; } - case SHOW_CHAR_PTR: + case SHOW_CHAR_PTR: { if (!(pos= *(char**) value)) pos= ""; @@ -2457,10 +2449,48 @@ int make_db_list(THD *thd, List<char> *files, mysql_data_home, NullS, 1) != FIND_FILES_OK); } +struct st_add_schema_table +{ + List<char> *files; + const char *wild; +}; + +static my_bool add_schema_table(THD *thd, st_plugin_int *plugin, + void* p_data) +{ + st_add_schema_table *data= (st_add_schema_table *)p_data; + List<char> *file_list= data->files; + const char *wild= data->wild; + ST_SCHEMA_TABLE *schema_table= (ST_SCHEMA_TABLE *)plugin->data; + DBUG_ENTER("add_schema_table"); + + if (schema_table->hidden) + DBUG_RETURN(0); + if (wild) + { + if (lower_case_table_names) + { + if (wild_case_compare(files_charset_info, + schema_table->table_name, + wild)) + DBUG_RETURN(0); + } + else if (wild_compare(schema_table->table_name, wild, 0)) + DBUG_RETURN(0); + } + + if (file_list->push_back(thd->strdup(schema_table->table_name))) + DBUG_RETURN(1); + + DBUG_RETURN(0); +} int schema_tables_add(THD *thd, List<char> *files, const char *wild) { ST_SCHEMA_TABLE *tmp_schema_table= schema_tables; + st_add_schema_table add_data; + DBUG_ENTER("schema_tables_add"); + for (; tmp_schema_table->table_name; tmp_schema_table++) { if (tmp_schema_table->hidden) @@ -2478,9 +2508,16 @@ int schema_tables_add(THD *thd, List<char> *files, const char *wild) continue; } if (files->push_back(thd->strdup(tmp_schema_table->table_name))) - return 1; + DBUG_RETURN(1); } - return 0; + + add_data.files= files; + add_data.wild= wild; + if (plugin_foreach(thd, add_schema_table, + MYSQL_INFORMATION_SCHEMA_PLUGIN, &add_data)) + DBUG_RETURN(1); + + DBUG_RETURN(0); } @@ -2495,26 +2532,27 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) ST_SCHEMA_TABLE *schema_table= tables->schema_table; SELECT_LEX sel; INDEX_FIELD_VALUES idx_field_vals; - char path[FN_REFLEN], *end, *base_name, *orig_base_name, *file_name; + char path[FN_REFLEN], *base_name, *orig_base_name, *file_name; uint len; bool with_i_schema; enum enum_schema_tables schema_table_idx; List<char> bases; List_iterator_fast<char> it(bases); COND *partial_cond; - Security_context *sctx= thd->security_ctx; uint derived_tables= lex->derived_tables; int error= 1; enum legacy_db_type not_used; Open_tables_state open_tables_state_backup; bool save_view_prepare_mode= lex->view_prepare_mode; Query_tables_list query_tables_list_backup; - lex->view_prepare_mode= TRUE; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + Security_context *sctx= thd->security_ctx; +#endif DBUG_ENTER("get_all_tables"); - LINT_INIT(end); LINT_INIT(len); + lex->view_prepare_mode= TRUE; lex->reset_n_backup_query_tables_list(&query_tables_list_backup); /* @@ -2603,7 +2641,6 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) else { len= build_table_filename(path, sizeof(path), base_name, "", "", 0); - end= path + len; len= FN_LEN - len; find_files_result res= find_files(thd, &files, base_name, path, idx_field_vals.table_value, 0); @@ -2653,7 +2690,9 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) } else { - my_snprintf(end, len, "/%s%s", file_name, reg_ext); + build_table_filename(path, sizeof(path), + base_name, file_name, reg_ext, 0); + switch (mysql_frm_type(thd, path, ¬_used)) { case FRMTYPE_ERROR: table->field[3]->store(STRING_WITH_LEN("ERROR"), @@ -2750,11 +2789,12 @@ int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond) INDEX_FIELD_VALUES idx_field_vals; List<char> files; char *file_name; - uint length; bool with_i_schema; HA_CREATE_INFO create; TABLE *table= tables->table; +#ifndef NO_EMBEDDED_ACCESS_CHECKS Security_context *sctx= thd->security_ctx; +#endif DBUG_ENTER("fill_schema_shemata"); if (make_db_list(thd, &files, &idx_field_vals, @@ -2898,20 +2938,21 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables, if (file->stats.create_time) { thd->variables.time_zone->gmt_sec_to_TIME(&time, - file->stats.create_time); + (my_time_t) file->stats.create_time); table->field[14]->store_time(&time, MYSQL_TIMESTAMP_DATETIME); table->field[14]->set_notnull(); } if (file->stats.update_time) { thd->variables.time_zone->gmt_sec_to_TIME(&time, - file->stats.update_time); + (my_time_t) file->stats.update_time); table->field[15]->store_time(&time, MYSQL_TIMESTAMP_DATETIME); table->field[15]->set_notnull(); } if (file->stats.check_time) { - thd->variables.time_zone->gmt_sec_to_TIME(&time, file->stats.check_time); + thd->variables.time_zone->gmt_sec_to_TIME(&time, + (my_time_t) file->stats.check_time); table->field[16]->store_time(&time, MYSQL_TIMESTAMP_DATETIME); table->field[16]->set_notnull(); } @@ -3104,12 +3145,12 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, pos=(byte*) ((flags & NOT_NULL_FLAG) ? "NO" : "YES"); table->field[6]->store((const char*) pos, strlen((const char*) pos), cs); - is_blob= (field->type() == FIELD_TYPE_BLOB); + is_blob= (field->type() == MYSQL_TYPE_BLOB); if (field->has_charset() || is_blob || field->real_type() == MYSQL_TYPE_VARCHAR || // For varbinary type field->real_type() == MYSQL_TYPE_STRING) // For binary type { - uint32 octet_max_length= field->max_length(); + uint32 octet_max_length= field->max_display_length(); if (is_blob && octet_max_length != (uint32) 4294967295U) octet_max_length /= field->charset()->mbmaxlen; longlong char_max_len= is_blob ? @@ -3128,25 +3169,25 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, decimals= field->decimals(); switch (field->type()) { - case FIELD_TYPE_NEWDECIMAL: + case MYSQL_TYPE_NEWDECIMAL: field_length= ((Field_new_decimal*) field)->precision; break; - case FIELD_TYPE_DECIMAL: + case MYSQL_TYPE_DECIMAL: field_length= field->field_length - (decimals ? 2 : 1); break; - case FIELD_TYPE_TINY: - case FIELD_TYPE_SHORT: - case FIELD_TYPE_LONG: - case FIELD_TYPE_LONGLONG: - case FIELD_TYPE_INT24: - field_length= field->max_length() - 1; + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_INT24: + field_length= field->max_display_length() - 1; break; - case FIELD_TYPE_BIT: - field_length= field->max_length(); + case MYSQL_TYPE_BIT: + field_length= field->max_display_length(); decimals= -1; // return NULL break; - case FIELD_TYPE_FLOAT: - case FIELD_TYPE_DOUBLE: + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: field_length= field->field_length; if (decimals == NOT_FIXED_DEC) decimals= -1; // return NULL @@ -3377,7 +3418,7 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, restore_record(table, s->default_values); if (!wild || !wild[0] || !wild_compare(sp_name.ptr(), wild, 0)) { - int enum_idx= proc_table->field[5]->val_int(); + int enum_idx= (int) proc_table->field[5]->val_int(); table->field[3]->store(sp_name.ptr(), sp_name.length(), cs); get_field(thd->mem_root, proc_table->field[3], &tmp_string); table->field[0]->store(tmp_string.ptr(), tmp_string.length(), cs); @@ -3861,8 +3902,8 @@ static int get_schema_key_column_usage_record(THD *thd, show_table->file->get_foreign_key_list(thd, &f_key_list); FOREIGN_KEY_INFO *f_key_info; - List_iterator_fast<FOREIGN_KEY_INFO> it(f_key_list); - while ((f_key_info= it++)) + List_iterator_fast<FOREIGN_KEY_INFO> fkey_it(f_key_list); + while ((f_key_info= fkey_it++)) { LEX_STRING *f_info; LEX_STRING *r_info; @@ -3917,10 +3958,12 @@ static void collect_partition_expr(List<char> &field_list, String *str) } -static void store_schema_partitions_record(THD *thd, TABLE *table, +static void store_schema_partitions_record(THD *thd, TABLE *schema_table, + TABLE *showing_table, partition_element *part_elem, handler *file, uint part_id) { + TABLE* table= schema_table; CHARSET_INFO *cs= system_charset_info; PARTITION_INFO stat_info; TIME time; @@ -3938,20 +3981,21 @@ static void store_schema_partitions_record(THD *thd, TABLE *table, if (stat_info.create_time) { thd->variables.time_zone->gmt_sec_to_TIME(&time, - stat_info.create_time); + (my_time_t)stat_info.create_time); table->field[18]->store_time(&time, MYSQL_TIMESTAMP_DATETIME); table->field[18]->set_notnull(); } if (stat_info.update_time) { thd->variables.time_zone->gmt_sec_to_TIME(&time, - stat_info.update_time); + (my_time_t)stat_info.update_time); table->field[19]->store_time(&time, MYSQL_TIMESTAMP_DATETIME); table->field[19]->set_notnull(); } if (stat_info.check_time) { - thd->variables.time_zone->gmt_sec_to_TIME(&time, stat_info.check_time); + thd->variables.time_zone->gmt_sec_to_TIME(&time, + (my_time_t)stat_info.check_time); table->field[20]->store_time(&time, MYSQL_TIMESTAMP_DATETIME); table->field[20]->set_notnull(); } @@ -3971,11 +4015,22 @@ static void store_schema_partitions_record(THD *thd, TABLE *table, table->field[23]->store((longlong) part_elem->nodegroup_id, TRUE); else table->field[23]->store(STRING_WITH_LEN("default"), cs); + + table->field[24]->set_notnull(); if (part_elem->tablespace_name) table->field[24]->store(part_elem->tablespace_name, strlen(part_elem->tablespace_name), cs); else - table->field[24]->store(STRING_WITH_LEN("default"), cs); + { + char *ts= showing_table->file->get_tablespace_name(thd,0,0); + if(ts) + { + table->field[24]->store(ts, strlen(ts), cs); + my_free(ts, MYF(0)); + } + else + table->field[24]->set_null(); + } } return; } @@ -3990,7 +4045,6 @@ static int get_schema_partitions_record(THD *thd, struct st_table_list *tables, char buff[61]; String tmp_res(buff, sizeof(buff), cs); String tmp_str; - TIME time; TABLE *show_table= tables->table; handler *file; #ifdef WITH_PARTITION_STORAGE_ENGINE @@ -4014,8 +4068,6 @@ static int get_schema_partitions_record(THD *thd, struct st_table_list *tables, partition_element *part_elem; List_iterator<partition_element> part_it(part_info->partitions); uint part_pos= 0, part_id= 0; - uint no_parts= part_info->no_parts; - handler *part_file; restore_record(table, s->default_values); table->field[1]->store(base_name, strlen(base_name), cs); @@ -4097,8 +4149,6 @@ static int get_schema_partitions_record(THD *thd, struct st_table_list *tables, while ((part_elem= part_it++)) { - - table->field[3]->store(part_elem->partition_name, strlen(part_elem->partition_name), cs); table->field[3]->set_notnull(); @@ -4158,7 +4208,7 @@ static int get_schema_partitions_record(THD *thd, struct st_table_list *tables, table->field[6]->store((longlong) ++subpart_pos, TRUE); table->field[6]->set_notnull(); - store_schema_partitions_record(thd, table, subpart_elem, + store_schema_partitions_record(thd, table, show_table, subpart_elem, file, part_id); part_id++; if(schema_table_store_record(thd, table)) @@ -4167,7 +4217,7 @@ static int get_schema_partitions_record(THD *thd, struct st_table_list *tables, } else { - store_schema_partitions_record(thd, table, part_elem, + store_schema_partitions_record(thd, table, show_table, part_elem, file, part_id); part_id++; if(schema_table_store_record(thd, table)) @@ -4179,7 +4229,7 @@ static int get_schema_partitions_record(THD *thd, struct st_table_list *tables, else #endif { - store_schema_partitions_record(thd, table, 0, file, 0); + store_schema_partitions_record(thd, table, show_table, 0, file, 0); if(schema_table_store_record(thd, table)) DBUG_RETURN(1); } @@ -4187,6 +4237,7 @@ static int get_schema_partitions_record(THD *thd, struct st_table_list *tables, } +#ifdef NOT_USED static interval_type get_real_interval_type(interval_type i_type) { switch (i_type) { @@ -4230,6 +4281,8 @@ static interval_type get_real_interval_type(interval_type i_type) return INTERVAL_SECOND; } +#endif + /* Loads an event from mysql.event and copies it's data to a row of @@ -4495,8 +4548,10 @@ get_referential_constraints_record(THD *thd, struct st_table_list *tables, f_key_info->forein_id->length, cs); table->field[4]->store(f_key_info->referenced_db->str, f_key_info->referenced_db->length, cs); - table->field[5]->store(f_key_info->referenced_table->str, + table->field[10]->store(f_key_info->referenced_table->str, f_key_info->referenced_table->length, cs); + table->field[5]->store(f_key_info->referenced_key_name->str, + f_key_info->referenced_key_name->length, cs); table->field[6]->store(STRING_WITH_LEN("NONE"), cs); table->field[7]->store(f_key_info->update_method->str, f_key_info->update_method->length, cs); @@ -4509,6 +4564,44 @@ get_referential_constraints_record(THD *thd, struct st_table_list *tables, DBUG_RETURN(0); } +struct schema_table_ref +{ + const char *table_name; + ST_SCHEMA_TABLE *schema_table; +}; + + +/* + Find schema_tables elment by name + + SYNOPSIS + find_schema_table_in_plugin() + thd thread handler + plugin plugin + table_name table name + + RETURN + 0 table not found + 1 found the schema table +*/ +static my_bool find_schema_table_in_plugin(THD *thd, st_plugin_int *plugin, + void* p_table) +{ + schema_table_ref *p_schema_table= (schema_table_ref *)p_table; + const char* table_name= p_schema_table->table_name; + ST_SCHEMA_TABLE *schema_table= (ST_SCHEMA_TABLE *)plugin->data; + DBUG_ENTER("find_schema_table_in_plugin"); + + if (!my_strcasecmp(system_charset_info, + schema_table->table_name, + table_name)) { + p_schema_table->schema_table= schema_table; + DBUG_RETURN(1); + } + + DBUG_RETURN(0); +} + /* Find schema_tables elment by name @@ -4525,15 +4618,24 @@ get_referential_constraints_record(THD *thd, struct st_table_list *tables, ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name) { + schema_table_ref schema_table_a; ST_SCHEMA_TABLE *schema_table= schema_tables; + DBUG_ENTER("find_schema_table"); + for (; schema_table->table_name; schema_table++) { if (!my_strcasecmp(system_charset_info, schema_table->table_name, table_name)) - return schema_table; + DBUG_RETURN(schema_table); } - return 0; + + schema_table_a.table_name= table_name; + if (plugin_foreach(thd, find_schema_table_in_plugin, + MYSQL_INFORMATION_SCHEMA_PLUGIN, &schema_table_a)) + DBUG_RETURN(schema_table_a.schema_table); + + DBUG_RETURN(NULL); } @@ -4946,13 +5048,15 @@ int make_schema_select(THD *thd, SELECT_LEX *sel, SYNOPSIS get_schema_tables_result() join join which use schema tables + executed_place place where I_S table processed RETURN FALSE success TRUE error */ -bool get_schema_tables_result(JOIN *join) +bool get_schema_tables_result(JOIN *join, + enum enum_schema_table_state executed_place) { JOIN_TAB *tmp_join_tab= join->join_tab+join->tables; THD *thd= join->thd; @@ -4972,19 +5076,30 @@ bool get_schema_tables_result(JOIN *join) bool is_subselect= (&lex->unit != lex->current_select->master_unit() && lex->current_select->master_unit()->item); /* - The schema table is already processed and - the statement is not a subselect. - So we don't need to handle this table again. + If schema table is already processed and + the statement is not a subselect then + we don't need to fill this table again. + If schema table is already processed and + schema_table_state != executed_place then + table is already processed and + we should skip second data processing. */ - if (table_list->is_schema_table_processed && !is_subselect) + if (table_list->schema_table_state && + (!is_subselect || table_list->schema_table_state != executed_place)) continue; - if (is_subselect) // is subselect + /* + if table is used in a subselect and + table has been processed earlier with the same + 'executed_place' value then we should refresh the table. + */ + if (table_list->schema_table_state && is_subselect) { + table_list->table->file->extra(HA_EXTRA_NO_CACHE); table_list->table->file->extra(HA_EXTRA_RESET_STATE); table_list->table->file->delete_all_rows(); free_io_cache(table_list->table); - filesort_free_buffers(table_list->table); + filesort_free_buffers(table_list->table,1); table_list->table->null_row= 0; } else @@ -4995,10 +5110,10 @@ bool get_schema_tables_result(JOIN *join) { result= 1; join->error= 1; - table_list->is_schema_table_processed= TRUE; + table_list->schema_table_state= executed_place; break; } - table_list->is_schema_table_processed= TRUE; + table_list->schema_table_state= executed_place; } } thd->no_warnings_for_error= 0; @@ -5024,8 +5139,6 @@ static my_bool run_hton_fill_schema_files(THD *thd, st_plugin_int *plugin, int fill_schema_files(THD *thd, TABLE_LIST *tables, COND *cond) { - int i; - TABLE *table= tables->table; DBUG_ENTER("fill_schema_files"); struct run_hton_fill_schema_files_args args; @@ -5083,7 +5196,7 @@ int fill_schema_status(THD *thd, SHOW_VAR *variables, if (show_type == SHOW_SYS) { - show_type= ((sys_var*) value)->type(); + show_type= ((sys_var*) value)->show_type(); value= (char*) ((sys_var*) value)->value_ptr(thd, OPT_GLOBAL, &null_lex_str); } @@ -5250,7 +5363,7 @@ ST_FIELD_INFO columns_fields_info[]= {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"COLUMN_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Field"}, {"ORDINAL_POSITION", 21 , MYSQL_TYPE_LONG, 0, 0, 0}, - {"COLUMN_DEFAULT", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, "Default"}, + {"COLUMN_DEFAULT", MAX_FIELD_VARCHARLENGTH, MYSQL_TYPE_STRING, 0, 1, "Default"}, {"IS_NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null"}, {"DATA_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"CHARACTER_MAXIMUM_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, 0}, @@ -5544,7 +5657,7 @@ ST_FIELD_INFO partitions_fields_info[]= {"CHECKSUM", 21 , MYSQL_TYPE_LONG, 0, 1, 0}, {"PARTITION_COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, 0}, {"NODEGROUP", 12 , MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLESPACE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, + {"TABLESPACE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; @@ -5645,6 +5758,16 @@ ST_FIELD_INFO files_fields_info[]= {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; +void init_fill_schema_files_row(TABLE* table) +{ + int i; + for(i=0; files_fields_info[i].field_name!=NULL; i++) + table->field[i]->set_null(); + + table->field[IS_FILES_STATUS]->set_notnull(); + table->field[IS_FILES_STATUS]->store("NORMAL", 6, system_charset_info); +} + ST_FIELD_INFO referential_constraints_fields_info[]= { {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, @@ -5657,6 +5780,7 @@ ST_FIELD_INFO referential_constraints_fields_info[]= {"UPDATE_RULE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"DELETE_RULE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, + {"REFERENCED_TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; @@ -5741,3 +5865,54 @@ ST_SCHEMA_TABLE schema_tables[]= template class List_iterator_fast<char>; template class List<char>; #endif + +int initialize_schema_table(st_plugin_int *plugin) +{ + ST_SCHEMA_TABLE *schema_table; + DBUG_ENTER("initialize_schema_table"); + + if (!(schema_table= (ST_SCHEMA_TABLE *)my_malloc(sizeof(ST_SCHEMA_TABLE), + MYF(MY_WME | MY_ZEROFILL)))) + DBUG_RETURN(1); + /* Historical Requirement */ + plugin->data= schema_table; // shortcut for the future + if (plugin->plugin->init) + { + schema_table->create_table= create_schema_table; + schema_table->old_format= make_old_format; + schema_table->idx_field1= -1, + schema_table->idx_field2= -1; + + if (plugin->plugin->init(schema_table)) + { + sql_print_error("Plugin '%s' init function returned error.", + plugin->name.str); + goto err; + } + schema_table->table_name= plugin->name.str; + } + + DBUG_RETURN(0); +err: + my_free((gptr)schema_table, MYF(0)); + DBUG_RETURN(1); +} + +int finalize_schema_table(st_plugin_int *plugin) +{ + ST_SCHEMA_TABLE *schema_table= (ST_SCHEMA_TABLE *)plugin->data; + DBUG_ENTER("finalize_schema_table"); + + if (schema_table && plugin->plugin->deinit) + { + DBUG_PRINT("info", ("Deinitializing plugin: '%s'", plugin->name.str)); + if (plugin->plugin->deinit(NULL)) + { + DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.", + plugin->name.str)); + } + my_free((gptr)schema_table, MYF(0)); + } + + DBUG_RETURN(0); +} diff --git a/sql/sql_show.h b/sql/sql_show.h index 681d1232b39..d5c3f3bf675 100644 --- a/sql/sql_show.h +++ b/sql/sql_show.h @@ -1,3 +1,17 @@ +/* Copyright (C) 2006 MySQL 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 + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SQL_SHOW_H #define SQL_SHOW_H @@ -10,6 +24,15 @@ struct st_table_list; typedef st_ha_create_information HA_CREATE_INFO; typedef st_table_list TABLE_LIST; +enum find_files_result { + FIND_FILES_OK, + FIND_FILES_OOM, + FIND_FILES_DIR +}; + +find_files_result find_files(THD *thd, List<char> *files, const char *db, + const char *path, const char *wild, bool dir); + int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, HA_CREATE_INFO *create_info_arg); int view_store_create_info(THD *thd, TABLE_LIST *table, String *buff); diff --git a/sql/sql_sort.h b/sql/sql_sort.h index 1831c4a2f3d..da28ca07e2c 100644 --- a/sql/sql_sort.h +++ b/sql/sql_sort.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_state.c b/sql/sql_state.c index 355b847f239..511dc65917b 100644 --- a/sql/sql_state.c +++ b/sql/sql_state.c @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 29b53560067..52af2f2dd90 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -608,27 +607,26 @@ skip: } /* -** replace substring with string -** If wrong parameter or not enough memory, do nothing + Replace substring with string + If wrong parameter or not enough memory, do nothing */ - bool String::replace(uint32 offset,uint32 arg_length,const String &to) { return replace(offset,arg_length,to.ptr(),to.length()); } bool String::replace(uint32 offset,uint32 arg_length, - const char *to,uint32 length) + const char *to, uint32 to_length) { - long diff = (long) length-(long) arg_length; + long diff = (long) to_length-(long) arg_length; if (offset+arg_length <= str_length) { if (diff < 0) { - if (length) - memcpy(Ptr+offset,to,length); - bmove(Ptr+offset+length,Ptr+offset+arg_length, + if (to_length) + memcpy(Ptr+offset,to,to_length); + bmove(Ptr+offset+to_length,Ptr+offset+arg_length, str_length-offset-arg_length); } else @@ -640,8 +638,8 @@ bool String::replace(uint32 offset,uint32 arg_length, bmove_upp(Ptr+str_length+diff,Ptr+str_length, str_length-offset-arg_length); } - if (length) - memcpy(Ptr+offset,to,length); + if (to_length) + memcpy(Ptr+offset,to,to_length); } str_length+=(uint32) diff; } @@ -1023,8 +1021,8 @@ void String::print(String *str) case '\r': str->append(STRING_WITH_LEN("\\r")); break; - case 26: //Ctrl-Z - str->append(STRING_WITH_LEN("\\z")); + case '\032': // Ctrl-Z + str->append(STRING_WITH_LEN("\\Z")); break; default: str->append(c); diff --git a/sql/sql_string.h b/sql/sql_string.h index a72b24ae9d0..128ed749b5f 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -85,7 +84,8 @@ public: { /* never called */ } ~String() { free(); } - inline void set_charset(CHARSET_INFO *charset) { str_charset= charset; } + inline void set_charset(CHARSET_INFO *charset_arg) + { str_charset= charset_arg; } inline CHARSET_INFO *charset() const { return str_charset; } inline uint32 length() const { return str_length;} inline uint32 alloced_length() const { return Alloced_length;} @@ -364,3 +364,9 @@ public: return (s->alloced && Ptr >= s->Ptr && Ptr < s->Ptr + s->str_length); } }; + +static inline bool check_if_only_end_space(CHARSET_INFO *cs, char *str, + char *end) +{ + return str+ cs->cset->scan(cs, str, end, MY_SEQ_SPACES) == end; +} diff --git a/sql/sql_table.cc b/sql/sql_table.cc index c024ee8ddbe..0697fdd79b4 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -37,10 +36,12 @@ static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end); static int copy_data_between_tables(TABLE *from,TABLE *to, List<create_field> &create, bool ignore, uint order_num, ORDER *order, - ha_rows *copied,ha_rows *deleted); + ha_rows *copied,ha_rows *deleted, + enum enum_enable_or_disable keys_onoff); + static bool prepare_blob_field(THD *thd, create_field *sql_field); static bool check_engine(THD *thd, const char *table_name, - HA_CREATE_INFO *create_info); + HA_CREATE_INFO *create_info); static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, List<create_field> *fields, List<Key> *keys, bool tmp_table, @@ -459,10 +460,10 @@ static bool write_ddl_log_header() global_ddl_log.num_entries); const_var= FN_LEN; int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_LEN_POS], - const_var); + (ulong) const_var); const_var= IO_SIZE; int4store(&global_ddl_log.file_entry_buf[DDL_LOG_IO_SIZE_POS], - const_var); + (ulong) const_var); if (write_ddl_log_file_entry(0UL)) { sql_print_error("Error writing ddl log header"); @@ -597,7 +598,6 @@ bool read_ddl_log_entry(uint read_entry, DDL_LOG_ENTRY *ddl_log_entry) static bool init_ddl_log() { - bool error= FALSE; char file_name[FN_REFLEN]; DBUG_ENTER("init_ddl_log"); @@ -1639,11 +1639,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, /* Disable drop of enabled log tables */ if (share && share->log_table && - ((!my_strcasecmp(system_charset_info, table->table_name, - "general_log") && opt_log && - logger.is_general_log_table_enabled()) || - (!my_strcasecmp(system_charset_info, table->table_name, "slow_log") - && opt_slow_log && logger.is_slow_log_table_enabled()))) + check_if_log_table(table->db_length, table->db, + table->table_name_length, table->table_name, 1)) { my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP"); DBUG_RETURN(1); @@ -2014,7 +2011,7 @@ void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval, int prepare_create_field(create_field *sql_field, uint *blob_columns, int *timestamps, int *timestamps_with_niladic, - uint table_flags) + longlong table_flags) { DBUG_ENTER("prepare_field"); @@ -2025,10 +2022,10 @@ int prepare_create_field(create_field *sql_field, DBUG_ASSERT(sql_field->charset); switch (sql_field->sql_type) { - case FIELD_TYPE_BLOB: - case FIELD_TYPE_MEDIUM_BLOB: - case FIELD_TYPE_TINY_BLOB: - case FIELD_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_LONG_BLOB: sql_field->pack_flag=FIELDFLAG_BLOB | pack_length_to_packflag(sql_field->pack_length - portable_sizeof_char_ptr); @@ -2038,7 +2035,7 @@ int prepare_create_field(create_field *sql_field, sql_field->unireg_check=Field::BLOB_FIELD; (*blob_columns)++; break; - case FIELD_TYPE_GEOMETRY: + case MYSQL_TYPE_GEOMETRY: #ifdef HAVE_SPATIAL if (!(table_flags & HA_CAN_GEOMETRY)) { @@ -2078,12 +2075,12 @@ int prepare_create_field(create_field *sql_field, } #endif /* fall through */ - case FIELD_TYPE_STRING: + case MYSQL_TYPE_STRING: sql_field->pack_flag=0; if (sql_field->charset->state & MY_CS_BINSORT) sql_field->pack_flag|=FIELDFLAG_BINARY; break; - case FIELD_TYPE_ENUM: + case MYSQL_TYPE_ENUM: sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) | FIELDFLAG_INTERVAL; if (sql_field->charset->state & MY_CS_BINSORT) @@ -2093,7 +2090,7 @@ int prepare_create_field(create_field *sql_field, sql_field->interval, sql_field->charset); break; - case FIELD_TYPE_SET: + case MYSQL_TYPE_SET: sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) | FIELDFLAG_BITFIELD; if (sql_field->charset->state & MY_CS_BINSORT) @@ -2103,19 +2100,19 @@ int prepare_create_field(create_field *sql_field, sql_field->interval, sql_field->charset); break; - case FIELD_TYPE_DATE: // Rest of string types - case FIELD_TYPE_NEWDATE: - case FIELD_TYPE_TIME: - case FIELD_TYPE_DATETIME: - case FIELD_TYPE_NULL: + case MYSQL_TYPE_DATE: // Rest of string types + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_NULL: sql_field->pack_flag=f_settype((uint) sql_field->sql_type); break; - case FIELD_TYPE_BIT: + case MYSQL_TYPE_BIT: /* We have sql_field->pack_flag already set here, see mysql_prepare_table(). */ break; - case FIELD_TYPE_NEWDECIMAL: + case MYSQL_TYPE_NEWDECIMAL: sql_field->pack_flag=(FIELDFLAG_NUMBER | (sql_field->flags & UNSIGNED_FLAG ? 0 : FIELDFLAG_DECIMAL) | @@ -2123,7 +2120,7 @@ int prepare_create_field(create_field *sql_field, FIELDFLAG_ZEROFILL : 0) | (sql_field->decimals << FIELDFLAG_DEC_SHIFT)); break; - case FIELD_TYPE_TIMESTAMP: + case MYSQL_TYPE_TIMESTAMP: /* We should replace old TIMESTAMP fields with their newer analogs */ if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD) { @@ -2248,10 +2245,10 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, */ if (sql_field->def && save_cs != sql_field->def->collation.collation && - (sql_field->sql_type == FIELD_TYPE_VAR_STRING || - sql_field->sql_type == FIELD_TYPE_STRING || - sql_field->sql_type == FIELD_TYPE_SET || - sql_field->sql_type == FIELD_TYPE_ENUM)) + (sql_field->sql_type == MYSQL_TYPE_VAR_STRING || + sql_field->sql_type == MYSQL_TYPE_STRING || + sql_field->sql_type == MYSQL_TYPE_SET || + sql_field->sql_type == MYSQL_TYPE_ENUM)) { Query_arena backup_arena; bool need_to_change_arena= !thd->stmt_arena->is_conventional(); @@ -2276,8 +2273,8 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, } } - if (sql_field->sql_type == FIELD_TYPE_SET || - sql_field->sql_type == FIELD_TYPE_ENUM) + if (sql_field->sql_type == MYSQL_TYPE_SET || + sql_field->sql_type == MYSQL_TYPE_ENUM) { uint32 dummy; CHARSET_INFO *cs= sql_field->charset; @@ -2298,14 +2295,14 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, interval= sql_field->interval= typelib(stmt_root, sql_field->interval_list); - List_iterator<String> it(sql_field->interval_list); + List_iterator<String> int_it(sql_field->interval_list); String conv, *tmp; char comma_buf[2]; int comma_length= cs->cset->wc_mb(cs, ',', (uchar*) comma_buf, (uchar*) comma_buf + sizeof(comma_buf)); DBUG_ASSERT(comma_length > 0); - for (uint i= 0; (tmp= it++); i++) + for (uint i= 0; (tmp= int_it++); i++) { uint lengthsp; if (String::needs_conversion(tmp->length(), tmp->charset(), @@ -2323,7 +2320,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, interval->type_lengths[i]); interval->type_lengths[i]= lengthsp; ((uchar *)interval->type_names[i])[lengthsp]= '\0'; - if (sql_field->sql_type == FIELD_TYPE_SET) + if (sql_field->sql_type == MYSQL_TYPE_SET) { if (cs->coll->instr(cs, interval->type_names[i], interval->type_lengths[i], @@ -2337,7 +2334,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, sql_field->interval_list.empty(); // Don't need interval_list anymore } - if (sql_field->sql_type == FIELD_TYPE_SET) + if (sql_field->sql_type == MYSQL_TYPE_SET) { uint32 field_length; if (sql_field->def != NULL) @@ -2373,10 +2370,10 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, calculate_interval_lengths(cs, interval, &dummy, &field_length); sql_field->length= field_length + (interval->count - 1); } - else /* FIELD_TYPE_ENUM */ + else /* MYSQL_TYPE_ENUM */ { uint32 field_length; - DBUG_ASSERT(sql_field->sql_type == FIELD_TYPE_ENUM); + DBUG_ASSERT(sql_field->sql_type == MYSQL_TYPE_ENUM); if (sql_field->def != NULL) { String str, *def= sql_field->def->val_str(&str); @@ -2406,7 +2403,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1); } - if (sql_field->sql_type == FIELD_TYPE_BIT) + if (sql_field->sql_type == MYSQL_TYPE_BIT) { sql_field->pack_flag= FIELDFLAG_NUMBER; if (file->ha_table_flags() & HA_CAN_BIT_FIELD) @@ -3062,7 +3059,7 @@ static bool prepare_blob_field(THD *thd, create_field *sql_field) MAX_FIELD_VARCHARLENGTH / sql_field->charset->mbmaxlen); DBUG_RETURN(1); } - sql_field->sql_type= FIELD_TYPE_BLOB; + sql_field->sql_type= MYSQL_TYPE_BLOB; sql_field->flags|= BLOB_FLAG; sprintf(warn_buff, ER(ER_AUTO_CONVERT), sql_field->field_name, (sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR", @@ -3073,7 +3070,7 @@ static bool prepare_blob_field(THD *thd, create_field *sql_field) if ((sql_field->flags & BLOB_FLAG) && sql_field->length) { - if (sql_field->sql_type == FIELD_TYPE_BLOB) + if (sql_field->sql_type == MYSQL_TYPE_BLOB) { /* The user has given a length to the blob column */ sql_field->sql_type= get_blob_type_from_length(sql_field->length); @@ -3101,11 +3098,11 @@ static bool prepare_blob_field(THD *thd, create_field *sql_field) void sp_prepare_create_field(THD *thd, create_field *sql_field) { - if (sql_field->sql_type == FIELD_TYPE_SET || - sql_field->sql_type == FIELD_TYPE_ENUM) + if (sql_field->sql_type == MYSQL_TYPE_SET || + sql_field->sql_type == MYSQL_TYPE_ENUM) { uint32 field_length, dummy; - if (sql_field->sql_type == FIELD_TYPE_SET) + if (sql_field->sql_type == MYSQL_TYPE_SET) { calculate_interval_lengths(sql_field->charset, sql_field->interval, &dummy, @@ -3113,7 +3110,7 @@ void sp_prepare_create_field(THD *thd, create_field *sql_field) sql_field->length= field_length + (sql_field->interval->count - 1); } - else /* FIELD_TYPE_ENUM */ + else /* MYSQL_TYPE_ENUM */ { calculate_interval_lengths(sql_field->charset, sql_field->interval, @@ -3123,7 +3120,7 @@ void sp_prepare_create_field(THD *thd, create_field *sql_field) set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1); } - if (sql_field->sql_type == FIELD_TYPE_BIT) + if (sql_field->sql_type == MYSQL_TYPE_BIT) { sql_field->pack_flag= FIELDFLAG_NUMBER | FIELDFLAG_TREAT_BIT_AS_CHAR; @@ -3419,16 +3416,11 @@ bool mysql_create_table_internal(THD *thd, { #ifdef FN_DEVCHAR /* check if the table name contains FN_DEVCHAR when defined */ - const char *start= alias; - while (*start != '\0') + if (strchr(alias, FN_DEVCHAR)) { - if (*start == FN_DEVCHAR) - { - my_error(ER_WRONG_TABLE_NAME, MYF(0), alias); - DBUG_RETURN(TRUE); - } - start++; - } + my_error(ER_WRONG_TABLE_NAME, MYF(0), alias); + DBUG_RETURN(TRUE); + } #endif path_length= build_table_filename(path, sizeof(path), db, alias, reg_ext, internal_tmp_table ? FN_IS_TMP : 0); @@ -3655,10 +3647,12 @@ make_unique_key_name(const char *field_name,KEY *start,KEY *end) flags flags for build_table_filename(). FN_FROM_IS_TMP old_name is temporary. FN_TO_IS_TMP new_name is temporary. + NO_FRM_RENAME Don't rename the FRM file + but only the table in the storage engine. RETURN - 0 OK - != 0 Error + FALSE OK + TRUE Error */ bool @@ -3707,7 +3701,7 @@ mysql_rename_table(handlerton *base, const char *old_db, if (!file || !(error=file->rename_table(from_base, to_base))) { - if (rename_file_ext(from,to,reg_ext)) + if (!(flags & NO_FRM_RENAME) && rename_file_ext(from,to,reg_ext)) { error=my_errno; /* Restore old file name */ @@ -3746,7 +3740,7 @@ static void wait_while_table_is_used(THD *thd,TABLE *table, enum ha_extra_function function) { DBUG_ENTER("wait_while_table_is_used"); - DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %u", + DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %lu", table->s->table_name.str, (ulong) table->s, table->db_stat, table->s->version)); @@ -4037,13 +4031,13 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT *), int (view_operator_func)(THD *, TABLE_LIST*)) { - TABLE_LIST *table, *save_next_global, *save_next_local; + TABLE_LIST *table; SELECT_LEX *select= &thd->lex->select_lex; List<Item> field_list; Item *item; Protocol *protocol= thd->protocol; LEX *lex= thd->lex; - int result_code; + int result_code, disable_logs= 0; DBUG_ENTER("mysql_admin_table"); if (end_active_trans(thd)) @@ -4071,29 +4065,48 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, thd->open_options|= extra_open_options; table->lock_type= lock_type; /* open only one table from local list of command */ - save_next_global= table->next_global; - table->next_global= 0; - save_next_local= table->next_local; - table->next_local= 0; - select->table_list.first= (byte*)table; - /* - Time zone tables and SP tables can be add to lex->query_tables list, - so it have to be prepared. - TODO: Investigate if we can put extra tables into argument instead of - using lex->query_tables - */ - lex->query_tables= table; - lex->query_tables_last= &table->next_global; - lex->query_tables_own_last= 0; - thd->no_warnings_for_error= no_warnings_for_error; - if (view_operator_func == NULL) - table->required_type=FRMTYPE_TABLE; - open_and_lock_tables(thd, table); - thd->no_warnings_for_error= 0; - table->next_global= save_next_global; - table->next_local= save_next_local; - thd->open_options&= ~extra_open_options; + { + TABLE_LIST *save_next_global, *save_next_local; + save_next_global= table->next_global; + table->next_global= 0; + save_next_local= table->next_local; + table->next_local= 0; + select->table_list.first= (byte*)table; + /* + Time zone tables and SP tables can be add to lex->query_tables list, + so it have to be prepared. + TODO: Investigate if we can put extra tables into argument instead of + using lex->query_tables + */ + lex->query_tables= table; + lex->query_tables_last= &table->next_global; + lex->query_tables_own_last= 0; + thd->no_warnings_for_error= no_warnings_for_error; + if (view_operator_func == NULL) + table->required_type=FRMTYPE_TABLE; + /* + If we want to perform an admin operation on the log table + (E.g. rename) and lock_type >= TL_READ_NO_INSERT disable + log tables + */ + + if (check_if_log_table(table->db_length, table->db, + table->table_name_length, + table->table_name, 1) && + lock_type >= TL_READ_NO_INSERT) + { + disable_logs= 1; + logger.lock(); + logger.tmp_close_log_tables(thd); + } + + open_and_lock_tables(thd, table); + thd->no_warnings_for_error= 0; + table->next_global= save_next_global; + table->next_local= save_next_local; + thd->open_options&= ~extra_open_options; + } if (prepare_func) { switch ((*prepare_func)(thd, table, check_opt)) { @@ -4154,7 +4167,6 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, goto send_result; } - table->table->pos_in_table_list= table; if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify) { char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE]; @@ -4404,11 +4416,24 @@ send_result_message: } send_eof(thd); + if (disable_logs) + { + if (logger.reopen_log_tables()) + my_error(ER_CANT_ACTIVATE_LOG, MYF(0)); + logger.unlock(); + } DBUG_RETURN(FALSE); err: ha_autocommit_or_rollback(thd, 1); close_thread_tables(thd); // Shouldn't be needed + /* enable logging back if needed */ + if (disable_logs) + { + if (logger.reopen_log_tables()) + my_error(ER_CANT_ACTIVATE_LOG, MYF(0)); + logger.unlock(); + } if (table) table->table=0; DBUG_RETURN(TRUE); @@ -4572,18 +4597,21 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, Table_ident *table_ident) { TABLE *tmp_table; - char src_path[FN_REFLEN], dst_path[FN_REFLEN], tmp_path[FN_REFLEN]; + char src_path[FN_REFLEN], dst_path[FN_REFLEN]; + char src_table_name_buff[FN_REFLEN], src_db_name_buff[FN_REFLEN]; uint dst_path_length; char *db= table->db; char *table_name= table->table_name; char *src_db; char *src_table= table_ident->table.str; int err; - bool res= TRUE; + bool res= TRUE, unlock_dst_table= FALSE; enum legacy_db_type not_used; HA_CREATE_INFO *create_info; - - TABLE_LIST src_tables_list; +#ifdef WITH_PARTITION_STORAGE_ENGINE + char tmp_path[FN_REFLEN]; +#endif + TABLE_LIST src_tables_list, dst_tables_list; DBUG_ENTER("mysql_create_like_table"); if (!(create_info= copy_create_info(lex_create_info))) @@ -4603,19 +4631,12 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, my_error(ER_WRONG_TABLE_NAME, MYF(0), src_table); DBUG_RETURN(TRUE); } - if (!src_db || check_db_name(src_db)) + if (!src_db || check_db_name(&table_ident->db)) { my_error(ER_WRONG_DB_NAME, MYF(0), src_db ? src_db : "NULL"); DBUG_RETURN(-1); } - bzero((gptr)&src_tables_list, sizeof(src_tables_list)); - src_tables_list.db= src_db; - src_tables_list.table_name= src_table; - - if (lock_and_wait_for_table_name(thd, &src_tables_list)) - goto err; - if ((tmp_table= find_temporary_table(thd, src_db, src_table))) strxmov(src_path, tmp_table->s->path.str, reg_ext, NullS); else @@ -4642,6 +4663,34 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, goto err; } + if (lower_case_table_names) + { + if (src_db) + { + strmake(src_db_name_buff, src_db, + min(sizeof(src_db_name_buff) - 1, table_ident->db.length)); + my_casedn_str(files_charset_info, src_db_name_buff); + src_db= src_db_name_buff; + } + if (src_table) + { + strmake(src_table_name_buff, src_table, + min(sizeof(src_table_name_buff) - 1, table_ident->table.length)); + my_casedn_str(files_charset_info, src_table_name_buff); + src_table= src_table_name_buff; + } + } + + bzero((gptr)&src_tables_list, sizeof(src_tables_list)); + src_tables_list.db= src_db; + src_tables_list.db_length= table_ident->db.length; + src_tables_list.lock_type= TL_READ; + src_tables_list.table_name= src_table; + src_tables_list.alias= src_table; + + if (simple_open_n_lock_tables(thd, &src_tables_list)) + DBUG_RETURN(TRUE); + /* Validate the destination table @@ -4696,8 +4745,9 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE)); #endif dst_path[dst_path_length - reg_ext_length]= '\0'; // Remove .frm + pthread_mutex_lock(&LOCK_open); err= ha_create_table(thd, dst_path, db, table_name, create_info, 1); - + pthread_mutex_unlock(&LOCK_open); if (create_info->options & HA_LEX_CREATE_TMP_TABLE) { if (err || !open_temporary_table(thd, dst_path, db, table_name, 1)) @@ -4745,18 +4795,31 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, char buf[2048]; String query(buf, sizeof(buf), system_charset_info); query.length(0); // Have to zero it since constructor doesn't - TABLE *table_ptr; - int error; + uint counter; /* - Let's open and lock the table: it will be closed (and - unlocked) by close_thread_tables() at the end of the - statement anyway. - */ - if (!(table_ptr= open_ltable(thd, table, TL_READ_NO_INSERT))) + Here we open the destination table. This is needed for + store_create_info() to work. The table will be closed + by close_thread_tables() at the end of the statement. + */ + if (open_tables(thd, &table, &counter, 0)) + goto err; + + bzero((gptr)&dst_tables_list, sizeof(dst_tables_list)); + dst_tables_list.db= table->db; + dst_tables_list.table_name= table->table_name; + + /* + lock destination table name, to make sure that nobody + can drop/alter the table while we execute store_create_info() + */ + if (lock_and_wait_for_table_name(thd, &dst_tables_list)) goto err; + else + unlock_dst_table= TRUE; - int result= store_create_info(thd, table, &query, create_info); + IF_DBUG(int result=) store_create_info(thd, table, &query, + create_info); DBUG_ASSERT(result == 0); // store_create_info() always return 0 write_bin_log(thd, TRUE, query.ptr(), query.length()); @@ -4788,9 +4851,12 @@ table_exists: my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name); err: - pthread_mutex_lock(&LOCK_open); - unlock_table_name(thd, &src_tables_list); - pthread_mutex_unlock(&LOCK_open); + if (unlock_dst_table) + { + pthread_mutex_lock(&LOCK_open); + unlock_table_name(thd, &dst_tables_list); + pthread_mutex_unlock(&LOCK_open); + } DBUG_RETURN(res); } @@ -5132,7 +5198,100 @@ static uint compare_tables(TABLE *table, List<create_field> *create_list, /* + Manages enabling/disabling of indexes for ALTER TABLE + + SYNOPSIS + alter_table_manage_keys() + table Target table + indexes_were_disabled Whether the indexes of the from table + were disabled + keys_onoff ENABLE | DISABLE | LEAVE_AS_IS + + RETURN VALUES + FALSE OK + TRUE Error +*/ + +static +bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled, + enum enum_enable_or_disable keys_onoff) +{ + int error= 0; + DBUG_ENTER("alter_table_manage_keys"); + DBUG_PRINT("enter", ("table=%p were_disabled=%d on_off=%d", + table, indexes_were_disabled, keys_onoff)); + + switch (keys_onoff) { + case ENABLE: + error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); + break; + case LEAVE_AS_IS: + if (!indexes_were_disabled) + break; + /* fall-through: disabled indexes */ + case DISABLE: + error= table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); + } + + if (error == HA_ERR_WRONG_COMMAND) + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), table->s->table_name); + error= 0; + } else if (error) + table->file->print_error(error, MYF(0)); + + DBUG_RETURN(error); +} + + +/* Alter table + + SYNOPSIS + mysql_alter_table() + thd Thread handle + new_db If there is a RENAME clause + new_name If there is a RENAME clause + lex_create_info Information from the parsing phase. Since some + clauses are common to CREATE and ALTER TABLE, the + data is stored in lex->create_info. The non-common + is stored in lex->alter_info. + table_list The table to change. + fields lex->create_list - List of fields to be changed, + added or dropped. + keys lex->key_list - List of keys to be changed, added or + dropped. + order_num How many ORDER BY fields has been specified. + order List of fields to ORDER BY. + ignore Whether we have ALTER IGNORE TABLE + alter_info Information from the parsing phase specific to ALTER + TABLE and not shared with CREATE TABLE. + do_send_ok Whether to call send_ok() on success. + + DESCRIPTION + This is a veery long function and is everything but the kitchen sink :) + It is used to alter a table and not only by ALTER TABLE but also + CREATE|DROP INDEX are mapped on this function. + + When the ALTER TABLE statement just does a RENAME or ENABLE|DISABLE KEYS, + or both, then this function short cuts its operation by renaming + the table and/or enabling/disabling the keys. In this case, the FRM is + not changed, directly by mysql_alter_table. However, if there is a + RENAME + change of a field, or an index, the short cut is not used. + See how `fields` is used to generate the new FRM regarding the structure + of the fields. The same is done for the indices of the table. + + Important is the fact, that this function tries to do as little work as + possible, by finding out whether a intermediate table is needed to copy + data into and when finishing the altering to use it as the original table. + For this reason the function compare_tables() is called, which decides + based on all kind of data how similar are the new and the original + tables. + + RETURN VALUES + FALSE OK + TRUE Error */ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, @@ -5143,15 +5302,15 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, ALTER_INFO *alter_info, bool do_send_ok) { TABLE *table,*new_table=0; - int error; + int error= 0; char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN]; char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias; - char index_file[FN_REFLEN], data_file[FN_REFLEN]; + char index_file[FN_REFLEN], data_file[FN_REFLEN], tablespace[FN_LEN]; char path[FN_REFLEN]; char reg_path[FN_REFLEN+1]; ha_rows copied,deleted; uint db_create_options, used_fields; - handlerton *old_db_type, *new_db_type; + handlerton *old_db_type, *new_db_type, *save_old_db_type; legacy_db_type table_type; HA_CREATE_INFO *create_info; frm_type_enum frm_type; @@ -5179,33 +5338,23 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, LINT_INIT(index_add_buffer); LINT_INIT(index_drop_buffer); - if (table_list && table_list->db && - !my_strcasecmp(system_charset_info, table_list->db, "mysql") && - table_list->table_name) + if (table_list && table_list->db && table_list->table_name) { - enum enum_table_kind { NOT_LOG_TABLE= 1, GENERAL_LOG, SLOW_LOG } - table_kind= NOT_LOG_TABLE; + int table_kind= 0; - if (!my_strcasecmp(system_charset_info, table_list->table_name, - "general_log")) - table_kind= GENERAL_LOG; - else - if (!my_strcasecmp(system_charset_info, table_list->table_name, - "slow_log")) - table_kind= SLOW_LOG; + table_kind= check_if_log_table(table_list->db_length, table_list->db, + table_list->table_name_length, + table_list->table_name, 0); /* Disable alter of enabled log tables */ - if ((table_kind == GENERAL_LOG && opt_log && - logger.is_general_log_table_enabled()) || - (table_kind == SLOW_LOG && opt_slow_log && - logger.is_slow_log_table_enabled())) + if (table_kind && logger.is_log_table_enabled(table_kind)) { my_error(ER_BAD_LOG_STATEMENT, MYF(0), "ALTER"); DBUG_RETURN(TRUE); } /* Disable alter of log tables to unsupported engine */ - if ((table_kind == GENERAL_LOG || table_kind == SLOW_LOG) && + if (table_kind && (lex_create_info->used_fields & HA_CREATE_USED_ENGINE) && (!lex_create_info->db_type || /* unknown engine */ !(lex_create_info->db_type->flags & HTON_SUPPORT_LOG_TABLES))) @@ -5234,6 +5383,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */ if (alter_info->tablespace_op != NO_TABLESPACE_OP) + /* Conditionally writes to binlog. */ DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list, alter_info->tablespace_op)); strxnmov(new_name_buff, sizeof (new_name_buff) - 1, mysql_data_home, "/", db, @@ -5254,16 +5404,18 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, { my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); - DBUG_RETURN(1); + DBUG_RETURN(TRUE); } if (wait_if_global_read_lock(thd,0,1)) - DBUG_RETURN(1); + DBUG_RETURN(TRUE); VOID(pthread_mutex_lock(&LOCK_open)); if (lock_table_names(thd, table_list)) + { + error= 1; goto view_err; + } - error=0; if (!do_rename(thd, table_list, new_db, new_name, new_name, 1)) { if (mysql_bin_log.is_open()) @@ -5388,13 +5540,59 @@ view_err: if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) && !table->s->tmp_table) // no need to touch frm { - error=0; - if (new_name != table_name || new_db != db) + switch (alter_info->keys_onoff) { + case LEAVE_AS_IS: + break; + case ENABLE: + /* + wait_while_table_is_used() ensures that table being altered is + opened only by this thread and that TABLE::TABLE_SHARE::version + of TABLE object corresponding to this table is 0. + The latter guarantees that no DML statement will open this table + until ALTER TABLE finishes (i.e. until close_thread_tables()) + while the fact that the table is still open gives us protection + from concurrent DDL statements. + */ + VOID(pthread_mutex_lock(&LOCK_open)); + wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); + VOID(pthread_mutex_unlock(&LOCK_open)); + error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); + /* COND_refresh will be signaled in close_thread_tables() */ + break; + case DISABLE: + VOID(pthread_mutex_lock(&LOCK_open)); + wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); + VOID(pthread_mutex_unlock(&LOCK_open)); + error=table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); + /* COND_refresh will be signaled in close_thread_tables() */ + break; + default: + DBUG_ASSERT(FALSE); + error= 0; + break; + } + if (error == HA_ERR_WRONG_COMMAND) + { + error= 0; + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), + table->alias); + } + + VOID(pthread_mutex_lock(&LOCK_open)); + /* + Unlike to the above case close_cached_table() below will remove ALL + instances of TABLE from table cache (it will also remove table lock + held by this thread). So to make actual table renaming and writing + to binlog atomic we have to put them into the same critical section + protected by LOCK_open mutex. This also removes gap for races between + access() and mysql_rename_table() calls. + */ + + if (!error && (new_name != table_name || new_db != db)) { thd->proc_info="rename"; - VOID(pthread_mutex_lock(&LOCK_open)); /* Then do a 'simple' rename of the table */ - error=0; if (!access(new_name_buff,F_OK)) { my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name); @@ -5415,38 +5613,16 @@ view_err: error= -1; } } - VOID(pthread_mutex_unlock(&LOCK_open)); - } - - if (!error) - { - switch (alter_info->keys_onoff) { - case LEAVE_AS_IS: - break; - case ENABLE: - VOID(pthread_mutex_lock(&LOCK_open)); - wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); - VOID(pthread_mutex_unlock(&LOCK_open)); - error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); - /* COND_refresh will be signaled in close_thread_tables() */ - break; - case DISABLE: - VOID(pthread_mutex_lock(&LOCK_open)); - wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); - VOID(pthread_mutex_unlock(&LOCK_open)); - error=table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); - /* COND_refresh will be signaled in close_thread_tables() */ - break; - } } if (error == HA_ERR_WRONG_COMMAND) { + error= 0; push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), table->alias); - error=0; } + if (!error) { write_bin_log(thd, TRUE, thd->query, thd->query_length); @@ -5458,12 +5634,13 @@ view_err: table->file->print_error(error, MYF(0)); error= -1; } - table_list->table=0; // For query cache + VOID(pthread_mutex_unlock(&LOCK_open)); + table_list->table= NULL; // For query cache query_cache_invalidate3(thd, table_list, 0); DBUG_RETURN(error); } - /* Full alter table */ + /* We have to do full alter table */ /* Let new create options override the old ones */ if (!(used_fields & HA_CREATE_USED_MIN_ROWS)) @@ -5477,6 +5654,15 @@ view_err: if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE)) create_info->key_block_size= table->s->key_block_size; + if (!create_info->tablespace && create_info->storage_media != HA_SM_MEMORY) + { + /* + Regular alter table of disk stored table (no tablespace/storage change) + Copy tablespace name + */ + if ((table->file->get_tablespace_name(thd, tablespace, FN_LEN))) + create_info->tablespace= tablespace; + } restore_record(table, s->default_values); // Empty record for DEFAULT List_iterator<Alter_drop> drop_it(alter_info->drop_list); List_iterator<create_field> def_it(fields); @@ -5550,7 +5736,7 @@ view_err: } if (alter) { - if (def->sql_type == FIELD_TYPE_BLOB) + if (def->sql_type == MYSQL_TYPE_BLOB) { my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change); DBUG_RETURN(TRUE); @@ -5982,8 +6168,8 @@ view_err: old data and index files. Create also symlinks to point at the new tables. Copy data. - At end, rename temporary tables and symlinks to temporary table - to final table name. + At end, rename intermediate tables, and symlinks to intermediate + table, to final table name. Remove old table and old symlinks If rename is made to another database: @@ -6044,6 +6230,7 @@ view_err: /* table is a normal table: Create temporary table in same directory */ build_table_filename(path, sizeof(path), new_db, tmp_name, "", FN_IS_TMP); + /* Open our intermediate table */ new_table=open_temporary_table(thd, path, new_db, tmp_name,0); } if (!new_table) @@ -6061,7 +6248,18 @@ view_err: new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; new_table->next_number_field=new_table->found_next_number_field; error=copy_data_between_tables(table, new_table, create_list, ignore, - order_num, order, &copied, &deleted); + order_num, order, &copied, &deleted, + alter_info->keys_onoff); + } + else + { + VOID(pthread_mutex_lock(&LOCK_open)); + wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); + table->file->ha_external_lock(thd, F_WRLCK); + alter_table_manage_keys(table, table->file->indexes_are_disabled(), + alter_info->keys_onoff); + table->file->ha_external_lock(thd, F_UNLCK); + VOID(pthread_mutex_unlock(&LOCK_open)); } thd->count_cuted_fields= CHECK_FIELD_IGNORE; @@ -6249,7 +6447,7 @@ view_err: if (new_table) { - /* close temporary table that will be the new table */ + /* Close the intermediate table that will be the new table */ intern_close_table(new_table); my_free((gptr) new_table,MYF(0)); } @@ -6263,7 +6461,7 @@ view_err: /* Data is copied. Now we rename the old table to a temp name, - rename the new one to the old name, remove all entries from the old table + rename the new one to the old name, remove all entries about the old table from the cache, free all locks, close the old table and remove it. */ @@ -6290,7 +6488,7 @@ view_err: { /* Win32 and InnoDB can't drop a table that is in use, so we must - close the original table at before doing the rename + close the original table before doing the rename */ table->s->version= 0; // Force removal of table def close_cached_table(thd, table); @@ -6304,6 +6502,21 @@ view_err: error=0; + save_old_db_type= old_db_type; + + /* + This leads to the storage engine (SE) not being notified for renames in + mysql_rename_table(), because we just juggle with the FRM and nothing + more. If we have an intermediate table, then we notify the SE that + it should become the actual table. Later, we will recycle the old table. + However, in case of ALTER TABLE RENAME there might be no intermediate + table. This is when the old and new tables are compatible, according to + compare_table(). Then, we need one additional call to + mysql_rename_table() with flag NO_FRM_RENAME, which does nothing else but + actual rename in the SE and the FRM is not touched. Note that, if the + table is renamed and the SE is also changed, then an intermediate table + is created and the additional call will not take place. + */ if (!need_copy_table) new_db_type=old_db_type= NULL; // this type cannot happen in regular ALTER if (mysql_rename_table(old_db_type, db, table_name, db, old_name, @@ -6313,8 +6526,11 @@ view_err: VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP)); } else if (mysql_rename_table(new_db_type,new_db,tmp_name,new_db, - new_alias, FN_FROM_IS_TMP) || + new_alias, FN_FROM_IS_TMP) || (new_name != table_name || new_db != db) && // we also do rename + (need_copy_table || + mysql_rename_table(save_old_db_type, db, table_name, new_db, + new_alias, NO_FRM_RENAME)) && Table_triggers_list::change_table_name(thd, db, table_name, new_db, new_alias)) { @@ -6325,6 +6541,7 @@ view_err: VOID(mysql_rename_table(old_db_type, db, old_name, db, alias, FN_FROM_IS_TMP)); } + if (error) { /* @@ -6343,6 +6560,15 @@ view_err: { if (! table) { + if (new_name != table_name || new_db != db) + { + table_list->alias= new_name; + table_list->table_name= new_name; + table_list->table_name_length= strlen(new_name); + table_list->db= new_db; + table_list->db_length= strlen(new_db); + } + VOID(pthread_mutex_unlock(&LOCK_open)); if (! (table= open_ltable(thd, table_list, TL_WRITE_ALLOW_READ))) goto err; @@ -6356,6 +6582,7 @@ view_err: goto err; } } + if (thd->lock || new_name != table_name || no_table_reopen) // True if WIN32 { /* @@ -6419,13 +6646,11 @@ view_err: thd->query, thd->query_length, db, table_name); - DBUG_ASSERT(!(mysql_bin_log.is_open() && thd->current_stmt_binlog_row_based && + DBUG_ASSERT(!(mysql_bin_log.is_open() && + thd->current_stmt_binlog_row_based && (create_info->options & HA_LEX_CREATE_TMP_TABLE))); write_bin_log(thd, TRUE, thd->query, thd->query_length); - /* - TODO RONM: This problem needs to handled for Berkeley DB partitions - as well - */ + if (ha_check_storage_engine_flag(old_db_type,HTON_FLUSH_AFTER_RENAME)) { /* @@ -6458,7 +6683,7 @@ end_temporary: thd->some_tables_deleted=0; DBUG_RETURN(FALSE); - err1: +err1: if (new_table) { /* close_temporary_table() frees the new_table pointer. */ @@ -6467,7 +6692,7 @@ end_temporary: else VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP)); - err: +err: DBUG_RETURN(TRUE); } /* mysql_alter_table */ @@ -6478,13 +6703,14 @@ copy_data_between_tables(TABLE *from,TABLE *to, bool ignore, uint order_num, ORDER *order, ha_rows *copied, - ha_rows *deleted) + ha_rows *deleted, + enum enum_enable_or_disable keys_onoff) { int error; Copy_field *copy,*copy_end; ulong found_count,delete_count; THD *thd= current_thd; - uint length; + uint length= 0; SORT_FIELD *sortorder; READ_RECORD info; TABLE_LIST tables; @@ -6512,6 +6738,9 @@ copy_data_between_tables(TABLE *from,TABLE *to, if (to->file->ha_external_lock(thd, F_WRLCK)) DBUG_RETURN(-1); + /* We need external lock before we can disable/enable keys */ + alter_table_manage_keys(to, from->file->indexes_are_disabled(), keys_onoff); + /* We can abort alter table for any table type */ thd->no_trans_update= 0; thd->abort_on_warning= !ignore && test(thd->variables.sql_mode & @@ -6610,7 +6839,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, uint key_nr= to->file->get_dup_key(error); if ((int) key_nr >= 0) { - const char *err_msg= ER(ER_DUP_ENTRY); + const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME); if (key_nr == 0 && (to->key_info[0].key_part[0].field->flags & AUTO_INCREMENT_FLAG)) @@ -6737,8 +6966,6 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, } else { - t->pos_in_table_list= table; - if (t->file->ha_table_flags() & HA_HAS_CHECKSUM && !(check_opt->flags & T_EXTEND)) protocol->store((ulonglong)t->file->checksum()); @@ -6780,7 +7007,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, for (uint i= 0; i < t->s->fields; i++ ) { Field *f= t->field[i]; - if ((f->type() == FIELD_TYPE_BLOB) || + if ((f->type() == MYSQL_TYPE_BLOB) || (f->type() == MYSQL_TYPE_VARCHAR)) { String tmp; diff --git a/sql/sql_tablespace.cc b/sql/sql_tablespace.cc index 470fa5bc862..84391a54642 100644 --- a/sql/sql_tablespace.cc +++ b/sql/sql_tablespace.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_test.cc b/sql/sql_test.cc index c4c40ea63c8..9e30cf5878c 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -74,17 +73,18 @@ void print_cached_tables(void) uint idx,count,unused; TABLE *start_link,*lnk; + /* purecov: begin tested */ VOID(pthread_mutex_lock(&LOCK_open)); - puts("DB Table Version Thread L.thread Open Lock"); + puts("DB Table Version Thread Open Lock"); for (idx=unused=0 ; idx < open_cache.records ; idx++) { TABLE *entry=(TABLE*) hash_element(&open_cache,idx); - printf("%-14.14s %-32s%6ld%8ld%10ld%6d %s\n", - entry->s->db.str, entry->s->table_name.str, entry->s->version, + printf("%-14.14s %-32s%6ld%8ld%6d %s\n", + entry->s->db.str, entry->s->table_name.str, entry->s->version, entry->in_use ? entry->in_use->thread_id : 0L, - entry->in_use ? entry->in_use->dbug_thread_id : 0L, - entry->db_stat ? 1 : 0, entry->in_use ? lock_descriptions[(int)entry->reginfo.lock_type] : "Not in use"); + entry->db_stat ? 1 : 0, + entry->in_use ? lock_descriptions[(int)entry->reginfo.lock_type] : "Not in use"); if (!entry->in_use) unused++; } @@ -111,6 +111,7 @@ void print_cached_tables(void) printf("Error: File hash table is corrupted\n"); fflush(stdout); VOID(pthread_mutex_unlock(&LOCK_open)); + /* purecov: end */ return; } @@ -248,14 +249,15 @@ print_plan(JOIN* join, uint idx, double record_count, double read_time, if (join->best_read == DBL_MAX) { fprintf(DBUG_FILE, - "%s; idx:%u, best: DBL_MAX, atime: %g, itime: %g, count: %g\n", - info, idx, current_read_time, read_time, record_count); + "%s; idx: %u best: DBL_MAX atime: %g itime: %g count: %g\n", + info, idx, current_read_time, read_time, record_count); } else { fprintf(DBUG_FILE, - "%s; idx:%u, best: %g, accumulated: %g, increment: %g, count: %g\n", - info, idx, join->best_read, current_read_time, read_time, record_count); + "%s; idx :%u best: %g accumulated: %g increment: %g count: %g\n", + info, idx, join->best_read, current_read_time, read_time, + record_count); } /* Print the tables in JOIN->positions */ diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index acb7d5b61df..311bd089c64 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -36,17 +35,17 @@ static File_option triggers_file_parameters[]= { { { C_STRING_WITH_LEN("triggers") }, - offsetof(class Table_triggers_list, definitions_list), + my_offsetof(class Table_triggers_list, definitions_list), FILE_OPTIONS_STRLIST }, { { C_STRING_WITH_LEN("sql_modes") }, - offsetof(class Table_triggers_list, definition_modes_list), + my_offsetof(class Table_triggers_list, definition_modes_list), FILE_OPTIONS_ULLLIST }, { { C_STRING_WITH_LEN("definers") }, - offsetof(class Table_triggers_list, definers_list), + my_offsetof(class Table_triggers_list, definers_list), FILE_OPTIONS_STRLIST }, { { 0, 0 }, 0, FILE_OPTIONS_STRING } @@ -55,7 +54,7 @@ static File_option triggers_file_parameters[]= File_option sql_modes_parameters= { { C_STRING_WITH_LEN("sql_modes") }, - offsetof(class Table_triggers_list, definition_modes_list), + my_offsetof(class Table_triggers_list, definition_modes_list), FILE_OPTIONS_ULLLIST }; @@ -107,8 +106,6 @@ const LEX_STRING trg_event_type_names[]= }; -static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig); - class Handle_old_incorrect_sql_modes_hook: public Unknown_key_hook { private: @@ -156,6 +153,13 @@ private: */ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) { + /* + FIXME: The code below takes too many different paths depending on the + 'create' flag, so that the justification for a single function + 'mysql_create_or_drop_trigger', compared to two separate functions + 'mysql_create_trigger' and 'mysql_drop_trigger' is not apparent. + This is a good candidate for a minor refactoring. + */ TABLE *table; bool result= TRUE; String stmt_query; @@ -181,10 +185,6 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) DBUG_RETURN(TRUE); } - if (!create && - !(tables= add_table_for_trigger(thd, thd->lex->spname))) - DBUG_RETURN(TRUE); - /* We don't allow creating triggers on tables in the 'mysql' schema */ @@ -194,30 +194,13 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) DBUG_RETURN(TRUE); } - /* We should have only one table in table list. */ - DBUG_ASSERT(tables->next_global == 0); - - /* - Check that the user has TRIGGER privilege on the subject table. - */ - { - bool err_status; - TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last; - thd->lex->query_tables_own_last= 0; - - err_status= check_table_access(thd, TRIGGER_ACL, tables, 0); - - thd->lex->query_tables_own_last= save_query_tables_own_last; - - if (err_status) - DBUG_RETURN(TRUE); - } - /* There is no DETERMINISTIC clause for triggers, so can't check it. But a trigger can in theory be used to do nasty things (if it supported - DROP for example) so we do the check for privileges. Triggers have the - same nature as functions regarding binlogging: their body is implicitely + DROP for example) so we do the check for privileges. For now there is + already a stronger test right above; but when this stronger test will + be removed, the test below will hold. Because triggers have the same + nature as functions regarding binlogging: their body is implicitly binlogged, so they share the same danger, so trust_function_creators applies to them too. */ @@ -228,24 +211,68 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) DBUG_RETURN(TRUE); } - /* We do not allow creation of triggers on temporary tables. */ - if (create && find_temporary_table(thd, tables)) - { - my_error(ER_TRG_ON_VIEW_OR_TEMP_TABLE, MYF(0), tables->alias); - DBUG_RETURN(TRUE); - } - /* We don't want perform our operations while global read lock is held - so we have to wait until its end and then prevent it from occuring + so we have to wait until its end and then prevent it from occurring again until we are done. (Acquiring LOCK_open is not enough because - global read lock is held without helding LOCK_open). + global read lock is held without holding LOCK_open). */ if (wait_if_global_read_lock(thd, 0, 1)) DBUG_RETURN(TRUE); VOID(pthread_mutex_lock(&LOCK_open)); + if (!create) + { + bool if_exists= thd->lex->drop_if_exists; + + if (add_table_for_trigger(thd, thd->lex->spname, if_exists, & tables)) + goto end; + + if (!tables) + { + DBUG_ASSERT(if_exists); + /* + Since the trigger does not exist, there is no associated table, + and therefore : + - no TRIGGER privileges to check, + - no trigger to drop, + - no table to lock/modify, + so the drop statement is successful. + */ + result= FALSE; + /* Still, we need to log the query ... */ + stmt_query.append(thd->query, thd->query_length); + goto end; + } + } + + /* + Check that the user has TRIGGER privilege on the subject table. + */ + { + bool err_status; + TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last; + thd->lex->query_tables_own_last= 0; + + err_status= check_table_access(thd, TRIGGER_ACL, tables, 0); + + thd->lex->query_tables_own_last= save_query_tables_own_last; + + if (err_status) + goto end; + } + + /* We should have only one table in table list. */ + DBUG_ASSERT(tables->next_global == 0); + + /* We do not allow creation of triggers on temporary tables. */ + if (create && find_temporary_table(thd, tables->db, tables->table_name)) + { + my_error(ER_TRG_ON_VIEW_OR_TEMP_TABLE, MYF(0), tables->alias); + goto end; + } + if (lock_table_names(thd, tables)) goto end; @@ -276,8 +303,6 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) table->triggers->drop_trigger(thd, tables, &stmt_query)); end: - VOID(pthread_mutex_unlock(&LOCK_open)); - start_waiting_global_read_lock(thd); if (!result) { @@ -286,13 +311,16 @@ end: thd->clear_error(); /* Such a statement can always go directly to binlog, no trans cache. */ - Query_log_event qinfo(thd, stmt_query.ptr(), stmt_query.length(), 0, - FALSE); - mysql_bin_log.write(&qinfo); + thd->binlog_query(THD::STMT_QUERY_TYPE, + stmt_query.ptr(), stmt_query.length(), FALSE, FALSE); } + } + VOID(pthread_mutex_unlock(&LOCK_open)); + start_waiting_global_read_lock(thd); + + if (!result) send_ok(thd); - } DBUG_RETURN(result); } @@ -308,7 +336,7 @@ end: tables - table list containing one open table for which the trigger is created. stmt_query - [OUT] after successful return, this string contains - well-formed statement for creation this trigger. + well-formed statement for creating this trigger. NOTE - Assumes that trigger name is fully qualified. @@ -329,7 +357,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, TABLE *table= tables->table; char file_buff[FN_REFLEN], trigname_buff[FN_REFLEN]; LEX_STRING file, trigname_file; - LEX_STRING *trg_def, *name; + LEX_STRING *trg_def; LEX_STRING definer_user; LEX_STRING definer_host; ulonglong *trg_sql_mode; @@ -348,7 +376,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, } /* We don't allow creation of several triggers of the same type yet */ - if (bodies[lex->trg_chistics.event][lex->trg_chistics.action_time]) + if (bodies[lex->trg_chistics.event][lex->trg_chistics.action_time] != 0) { my_error(ER_NOT_SUPPORTED_YET, MYF(0), "multiple triggers with the same action time" @@ -641,7 +669,7 @@ static bool save_trigger_file(Table_triggers_list *triggers, const char *db, tables - table list containing one open table for which trigger is dropped. stmt_query - [OUT] after successful return, this string contains - well-formed statement for creation this trigger. + well-formed statement for deleting this trigger. RETURN VALUE False - success @@ -749,7 +777,7 @@ bool Table_triggers_list::prepare_record1_accessors(TABLE *table) table == (*fld)->table))) return 1; (*old_fld)->move_field_offset((my_ptrdiff_t)(table->record[1] - - table->record[0])); + table->record[0])); } *old_fld= 0; @@ -767,8 +795,8 @@ bool Table_triggers_list::prepare_record1_accessors(TABLE *table) void Table_triggers_list::set_table(TABLE *new_table) { - table= new_table; - for (Field **field= table->triggers->record1_field ; *field ; field++) + trigger_table= new_table; + for (Field **field= new_table->triggers->record1_field ; *field ; field++) { (*field)->table= (*field)->orig_table= new_table; (*field)->table_name= &new_table->alias; @@ -844,7 +872,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, DBUG_RETURN(1); List_iterator_fast<LEX_STRING> it(triggers->definitions_list); - LEX_STRING *trg_create_str, *trg_name_str; + LEX_STRING *trg_create_str; ulonglong *trg_sql_mode; if (triggers->definition_modes_list.is_empty() && @@ -953,15 +981,12 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, thd->spcont= 0; if (MYSQLparse((void *)thd) || thd->is_fatal_error) { - /* - Free lex associated resources. - QQ: Do we really need all this stuff here ? - */ - delete lex.sphead; + /* Currently sphead is always deleted in case of a parse error */ + DBUG_ASSERT(lex.sphead == 0); goto err_with_lex_cleanup; } - lex.sphead->set_info(0, 0, &lex.sp_chistics, *trg_sql_mode); + lex.sphead->set_info(0, 0, &lex.sp_chistics, (ulong) *trg_sql_mode); triggers->bodies[lex.trg_chistics.event] [lex.trg_chistics.action_time]= lex.sphead; @@ -1140,13 +1165,17 @@ bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event, mysql_table_for_trigger() thd - current thread context trig - identifier for trigger + if_exists - treat a not existing trigger as a warning if TRUE + table - pointer to TABLE_LIST object for the table trigger (output) RETURN VALUE - 0 - error - # - pointer to TABLE_LIST object for the table + 0 Success + 1 Error */ -static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig) +int +add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists, + TABLE_LIST **table) { LEX *lex= thd->lex; char path_buff[FN_REFLEN]; @@ -1157,6 +1186,7 @@ static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig) path_buff, &trigname.trigger_table); DBUG_ENTER("add_table_for_trigger"); + DBUG_ASSERT(table != NULL); path.length= build_table_filename(path_buff, FN_REFLEN-1, trig->m_db.str, trig->m_name.str, @@ -1165,30 +1195,45 @@ static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig) if (access(path_buff, F_OK)) { + if (if_exists) + { + push_warning_printf(thd, + MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_TRG_DOES_NOT_EXIST, + ER(ER_TRG_DOES_NOT_EXIST)); + *table= NULL; + DBUG_RETURN(0); + } + my_error(ER_TRG_DOES_NOT_EXIST, MYF(0)); - DBUG_RETURN(0); + DBUG_RETURN(1); } if (!(parser= sql_parse_prepare(&path, thd->mem_root, 1))) - DBUG_RETURN(0); + DBUG_RETURN(1); if (!is_equal(&trigname_file_type, parser->type())) { my_error(ER_WRONG_OBJECT, MYF(0), trig->m_name.str, trigname_file_ext+1, "TRIGGERNAME"); - DBUG_RETURN(0); + DBUG_RETURN(1); } if (parser->parse((gptr)&trigname, thd->mem_root, trigname_file_parameters, 1, &trigger_table_hook)) - DBUG_RETURN(0); + DBUG_RETURN(1); /* We need to reset statement table list to be PS/SP friendly. */ lex->query_tables= 0; lex->query_tables_last= &lex->query_tables; - DBUG_RETURN(sp_add_to_query_tables(thd, lex, trig->m_db.str, - trigname.trigger_table.str, TL_IGNORE)); + *table= sp_add_to_query_tables(thd, lex, trig->m_db.str, + trigname.trigger_table.str, TL_IGNORE); + + if (! *table) + DBUG_RETURN(1); + + DBUG_RETURN(0); } @@ -1282,7 +1327,6 @@ Table_triggers_list::change_table_name_in_triggers(THD *thd, { char path_buff[FN_REFLEN]; LEX_STRING *def, *on_table_name, new_def; - ulonglong *sql_mode; ulong save_sql_mode= thd->variables.sql_mode; List_iterator_fast<LEX_STRING> it_def(definitions_list); List_iterator_fast<LEX_STRING> it_on_table_name(on_table_names_list); @@ -1296,7 +1340,7 @@ Table_triggers_list::change_table_name_in_triggers(THD *thd, while ((def= it_def++)) { on_table_name= it_on_table_name++; - thd->variables.sql_mode= *(it_mode++); + thd->variables.sql_mode= (ulong) *(it_mode++); /* Construct CREATE TRIGGER statement with new table name. */ buff.length(0); @@ -1312,7 +1356,8 @@ Table_triggers_list::change_table_name_in_triggers(THD *thd, It is OK to allocate some memory on table's MEM_ROOT since this table instance will be thrown out at the end of rename anyway. */ - new_def.str= memdup_root(&table->mem_root, buff.ptr(), buff.length()); + new_def.str= memdup_root(&trigger_table->mem_root, buff.ptr(), + buff.length()); new_def.length= buff.length(); on_table_name->str= new_def.str + before_on_len; on_table_name->length= on_q_table_name_len; @@ -1490,12 +1535,12 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event, if (old_row_is_record1) { old_field= record1_field; - new_field= table->field; + new_field= trigger_table->field; } else { new_field= record1_field; - old_field= table->field; + old_field= trigger_table->field; } #ifndef NO_EMBEDDED_ACCESS_CHECKS Security_context *save_ctx; @@ -1511,7 +1556,8 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event, fill_effective_table_privileges(thd, &subject_table_grants[event][time_type], - table->s->db.str, table->s->table_name.str); + trigger_table->s->db.str, + trigger_table->s->table_name.str); /* Check that the definer has TRIGGER privilege on the subject table. */ @@ -1522,7 +1568,7 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event, my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), priv_desc, thd->security_ctx->priv_user, thd->security_ctx->host_or_ip, - table->s->table_name.str); + trigger_table->s->table_name.str); sp_restore_security_context(thd, save_ctx); return TRUE; @@ -1531,7 +1577,7 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event, thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER); err_status= sp_trigger->execute_trigger - (thd, table->s->db.str, table->s->table_name.str, + (thd, trigger_table->s->db.str, trigger_table->s->table_name.str, &subject_table_grants[event][time_type]); thd->restore_sub_statement_state(&statement_state); @@ -1572,13 +1618,13 @@ void Table_triggers_list::mark_fields_used(trg_event_type event) /* We cannot mark fields which does not present in table. */ if (trg_field->field_idx != (uint)-1) { - bitmap_set_bit(table->read_set, trg_field->field_idx); + bitmap_set_bit(trigger_table->read_set, trg_field->field_idx); if (trg_field->get_settable_routine_parameter()) - bitmap_set_bit(table->write_set, trg_field->field_idx); + bitmap_set_bit(trigger_table->write_set, trg_field->field_idx); } } } - table->file->column_bitmaps_signal(); + trigger_table->file->column_bitmaps_signal(); } @@ -1611,7 +1657,7 @@ Handle_old_incorrect_sql_modes_hook::process_unknown_string(char *&unknown_key, char *end) { DBUG_ENTER("Handle_old_incorrect_sql_modes_hook::process_unknown_string"); - DBUG_PRINT("info", ("unknown key:%60s", unknown_key)); + DBUG_PRINT("info", ("unknown key: %60s", unknown_key)); if (unknown_key + INVALID_SQL_MODES_LENGTH + 1 < end && unknown_key[INVALID_SQL_MODES_LENGTH] == '=' && @@ -1653,7 +1699,7 @@ process_unknown_string(char *&unknown_key, gptr base, MEM_ROOT *mem_root, char *end) { DBUG_ENTER("Handle_old_incorrect_trigger_table_hook::process_unknown_string"); - DBUG_PRINT("info", ("unknown key:%60s", unknown_key)); + DBUG_PRINT("info", ("unknown key: %60s", unknown_key)); if (unknown_key + INVALID_TRIGGER_TABLE_LENGTH + 1 < end && unknown_key[INVALID_TRIGGER_TABLE_LENGTH] == '=' && diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index 09576f5e523..707fcc4e380 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -43,8 +42,9 @@ class Table_triggers_list: public Sql_alloc */ Field **new_field; Field **old_field; + /* TABLE instance for which this triggers list object was created */ - TABLE *table; + TABLE *trigger_table; /* Names of triggers. Should correspond to order of triggers on definitions_list, @@ -84,7 +84,7 @@ public: List<LEX_STRING> definers_list; Table_triggers_list(TABLE *table_arg): - record1_field(0), table(table_arg) + record1_field(0), trigger_table(table_arg) { bzero((char *)bodies, sizeof(bodies)); bzero((char *)trigger_fields, sizeof(trigger_fields)); @@ -137,3 +137,7 @@ private: extern const LEX_STRING trg_action_time_type_names[]; extern const LEX_STRING trg_event_type_names[]; + +int +add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists, + TABLE_LIST **table); diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index f0d31965573..7dec58d9b6e 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -482,6 +481,8 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) TABLE *table; TABLE_LIST tables; udf_func *udf; + char *exact_name_str; + uint exact_name_len; DBUG_ENTER("mysql_drop_function"); if (!initialized) { @@ -495,6 +496,8 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str); goto err; } + exact_name_str= udf->name.str; + exact_name_len= udf->name.length; del_udf(udf); /* Close the handle if this was function that was found during boot or @@ -509,7 +512,7 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) if (!(table = open_ltable(thd,&tables,TL_WRITE))) goto err; table->use_all_columns(); - table->field[0]->store(udf_name->str, udf_name->length, system_charset_info); + table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin); if (!table->file->index_read_idx(table->record[0], 0, (byte*) table->field[0]->ptr, table->key_info[0].key_length, diff --git a/sql/sql_udf.h b/sql/sql_udf.h index 21cf735f5ab..3cd9343610c 100644 --- a/sql/sql_udf.h +++ b/sql/sql_udf.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2001, 2003-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 3e6a3944093..c3635e24407 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -112,7 +111,7 @@ bool select_union::flush() */ bool -select_union::create_result_table(THD *thd, List<Item> *column_types, +select_union::create_result_table(THD *thd_arg, List<Item> *column_types, bool is_union_distinct, ulonglong options, const char *alias) { @@ -120,7 +119,7 @@ select_union::create_result_table(THD *thd, List<Item> *column_types, tmp_table_param.init(); tmp_table_param.field_count= column_types->elements; - if (! (table= create_tmp_table(thd, &tmp_table_param, *column_types, + if (! (table= create_tmp_table(thd_arg, &tmp_table_param, *column_types, (ORDER*) 0, is_union_distinct, 1, options, HA_POS_ERROR, (char*) alias))) return TRUE; @@ -142,12 +141,14 @@ select_union::create_result_table(THD *thd, List<Item> *column_types, */ void -st_select_lex_unit::init_prepare_fake_select_lex(THD *thd) +st_select_lex_unit::init_prepare_fake_select_lex(THD *thd_arg) { - thd->lex->current_select= fake_select_lex; + thd_arg->lex->current_select= fake_select_lex; fake_select_lex->table_list.link_in_list((byte *)&result_table_list, (byte **) &result_table_list.next_local); + fake_select_lex->context.table_list= fake_select_lex->context.first_name_resolution_table= + fake_select_lex->get_table_list(); for (ORDER *order= (ORDER *)global_parameters->order_list.first; order; order=order->next) @@ -198,7 +199,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, DBUG_RETURN(FALSE); } prepared= 1; - res= FALSE; + saved_error= FALSE; thd_arg->lex->current_select= sl= first_sl; found_rows_for_union= first_sl->options & OPTION_FOUND_ROWS; @@ -239,23 +240,25 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, can_skip_order_by= is_union && !(sl->braces && sl->explicit_limit); - res= join->prepare(&sl->ref_pointer_array, - (TABLE_LIST*) sl->table_list.first, sl->with_wild, - sl->where, - (can_skip_order_by ? 0 : sl->order_list.elements) + - sl->group_list.elements, - can_skip_order_by ? - (ORDER*) 0 : (ORDER *)sl->order_list.first, - (ORDER*) sl->group_list.first, - sl->having, - (is_union ? (ORDER*) 0 : - (ORDER*) thd_arg->lex->proc_list.first), - sl, this); + saved_error= join->prepare(&sl->ref_pointer_array, + (TABLE_LIST*) sl->table_list.first, + sl->with_wild, + sl->where, + (can_skip_order_by ? 0 : + sl->order_list.elements) + + sl->group_list.elements, + can_skip_order_by ? + (ORDER*) 0 : (ORDER *)sl->order_list.first, + (ORDER*) sl->group_list.first, + sl->having, + (is_union ? (ORDER*) 0 : + (ORDER*) thd_arg->lex->proc_list.first), + sl, this); /* There are no * in the statement anymore (for PS) */ sl->with_wild= 0; last_procedure= join->procedure; - if ((res= (res || thd_arg->is_fatal_error))) + if (saved_error || (saved_error= thd_arg->is_fatal_error)) goto err; /* Use items list of underlaid select for derived tables to preserve @@ -350,12 +353,12 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, arena= thd->activate_stmt_arena_if_needed(&backup_arena); - res= table->fill_item_list(&item_list); + saved_error= table->fill_item_list(&item_list); if (arena) thd->restore_active_arena(arena, &backup_arena); - if (res) + if (saved_error) goto err; if (thd->stmt_arena->is_stmt_prepare()) @@ -374,7 +377,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, fake_select_lex->item_list= item_list; thd_arg->lex->current_select= fake_select_lex; - res= fake_select_lex->join-> + saved_error= fake_select_lex->join-> prepare(&fake_select_lex->ref_pointer_array, (TABLE_LIST*) fake_select_lex->table_list.first, 0, 0, @@ -399,7 +402,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, thd_arg->lex->current_select= lex_select_save; - DBUG_RETURN(res || thd_arg->is_fatal_error); + DBUG_RETURN(saved_error || thd_arg->is_fatal_error); err: thd_arg->lex->current_select= lex_select_save; @@ -433,7 +436,9 @@ bool st_select_lex_unit::exec() } /* re-enabling indexes for next subselect iteration */ if (union_distinct && table->file->enable_indexes(HA_KEY_SWITCH_ALL)) + { DBUG_ASSERT(0); + } } for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select()) { @@ -441,7 +446,7 @@ bool st_select_lex_unit::exec() thd->lex->current_select= sl; if (optimized) - res= sl->join->reinit(); + saved_error= sl->join->reinit(); else { set_limit(sl); @@ -464,9 +469,9 @@ bool st_select_lex_unit::exec() sl->join->select_options= (select_limit_cnt == HA_POS_ERROR || sl->braces) ? sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union; - res= sl->join->optimize(); + saved_error= sl->join->optimize(); } - if (!res) + if (!saved_error) { records_at_start= table->file->stats.records; sl->join->exec(); @@ -476,11 +481,11 @@ bool st_select_lex_unit::exec() DBUG_RETURN(TRUE); table->no_keyread=1; } - res= sl->join->error; + saved_error= sl->join->error; offset_limit_cnt= (ha_rows)(sl->offset_limit ? sl->offset_limit->val_uint() : 0); - if (!res) + if (!saved_error) { examined_rows+= thd->examined_row_count; if (union_result->flush()) @@ -490,13 +495,18 @@ bool st_select_lex_unit::exec() } } } - if (res) + if (saved_error) { thd->lex->current_select= lex_select_save; - DBUG_RETURN(res); + DBUG_RETURN(saved_error); } /* Needed for the following test and for records_at_start in next loop */ - table->file->info(HA_STATUS_VARIABLE); + int error= table->file->info(HA_STATUS_VARIABLE); + if(error) + { + table->file->print_error(error, MYF(0)); + DBUG_RETURN(1); + } if (found_rows_for_union && !sl->braces && select_limit_cnt != HA_POS_ERROR) { @@ -514,7 +524,7 @@ bool st_select_lex_unit::exec() optimized= 1; /* Send result to 'result' */ - res= TRUE; + saved_error= TRUE; { List<Item_func_match> empty_list; empty_list.empty(); @@ -555,17 +565,17 @@ bool st_select_lex_unit::exec() } join->init(thd, item_list, fake_select_lex->options, result); } - res= mysql_select(thd, &fake_select_lex->ref_pointer_array, - &result_table_list, - 0, item_list, NULL, - global_parameters->order_list.elements, - (ORDER*)global_parameters->order_list.first, - (ORDER*) NULL, NULL, (ORDER*) NULL, - fake_select_lex->options | SELECT_NO_UNLOCK, - result, this, fake_select_lex); + saved_error= mysql_select(thd, &fake_select_lex->ref_pointer_array, + &result_table_list, + 0, item_list, NULL, + global_parameters->order_list.elements, + (ORDER*)global_parameters->order_list.first, + (ORDER*) NULL, NULL, (ORDER*) NULL, + fake_select_lex->options | SELECT_NO_UNLOCK, + result, this, fake_select_lex); fake_select_lex->table_list.empty(); - if (!res) + if (!saved_error) { thd->limit_found_rows = (ulonglong)table->file->stats.records + add_rows; thd->examined_row_count+= examined_rows; @@ -577,7 +587,7 @@ bool st_select_lex_unit::exec() } } thd->lex->current_select= lex_select_save; - DBUG_RETURN(res); + DBUG_RETURN(saved_error); } @@ -613,6 +623,12 @@ bool st_select_lex_unit::cleanup() join->tables= 0; } error|= fake_select_lex->cleanup(); + if (fake_select_lex->order_list.elements) + { + ORDER *ord; + for (ord= (ORDER*)fake_select_lex->order_list.first; ord; ord= ord->next) + (*ord->item)->cleanup(); + } } DBUG_RETURN(error); @@ -654,18 +670,18 @@ void st_select_lex_unit::reinit_exec_mechanism() TRUE - error */ -bool st_select_lex_unit::change_result(select_subselect *result, +bool st_select_lex_unit::change_result(select_subselect *new_result, select_subselect *old_result) { bool res= FALSE; for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) { if (sl->join && sl->join->result == old_result) - if (sl->join->change_result(result)) + if (sl->join->change_result(new_result)) return TRUE; } if (fake_select_lex && fake_select_lex->join) - res= fake_select_lex->join->change_result(result); + res= fake_select_lex->join->change_result(new_result); return (res); } @@ -689,7 +705,17 @@ bool st_select_lex_unit::change_result(select_subselect *result, List<Item> *st_select_lex_unit::get_unit_column_types() { - bool is_union= test(first_select()->next_select()); + SELECT_LEX *sl= first_select(); + bool is_union= test(sl->next_select()); + bool is_procedure= test(sl->join->procedure); + + if (is_procedure) + { + /* Types for "SELECT * FROM t1 procedure analyse()" + are generated during execute */ + return &sl->join->procedure_fields_list; + } + if (is_union) { @@ -697,7 +723,8 @@ List<Item> *st_select_lex_unit::get_unit_column_types() /* Types are generated during prepare */ return &types; } - return &first_select()->item_list; + + return &sl->item_list; } bool st_select_lex::cleanup() @@ -717,6 +744,7 @@ bool st_select_lex::cleanup() { error= (bool) ((uint) error | (uint) lex_unit->cleanup()); } + non_agg_fields.empty(); DBUG_RETURN(error); } @@ -733,4 +761,3 @@ void st_select_lex::cleanup_all_joins(bool full) for (sl= unit->first_select(); sl; sl= sl->next_select()) sl->cleanup_all_joins(full); } - diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 1d119b99df0..5aea05a9d39 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -25,11 +24,9 @@ #include "sp_head.h" #include "sql_trigger.h" -static bool safe_update_on_fly(JOIN_TAB *join_tab); - /* Return 0 if row hasn't changed */ -static bool compare_record(TABLE *table) +bool compare_record(TABLE *table) { if (table->s->blob_fields + table->s->varchar_fields == 0) return cmp_record(table,record[1]); @@ -67,7 +64,6 @@ static bool check_fields(THD *thd, List<Item> &items) List_iterator<Item> it(items); Item *item; Item_field *field; - Name_resolution_context *context= &thd->lex->select_lex.context; while ((item= it++)) { @@ -118,7 +114,7 @@ int mysql_update(THD *thd, enum enum_duplicates handle_duplicates, bool ignore) { bool using_limit= limit != HA_POS_ERROR; - bool safe_update= thd->options & OPTION_SAFE_UPDATES; + bool safe_update= test(thd->options & OPTION_SAFE_UPDATES); bool used_key_is_modified, transactional_table, will_batch; bool can_compare_record; int res; @@ -137,6 +133,7 @@ int mysql_update(THD *thd, SELECT_LEX *select_lex= &thd->lex->select_lex; bool need_reopen; ulonglong id; + List<Item> all_fields; DBUG_ENTER("mysql_update"); for ( ; ; ) @@ -220,6 +217,10 @@ int mysql_update(THD *thd, DBUG_RETURN(1); /* purecov: inspected */ } + if (select_lex->inner_refs_list.elements && + fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array)) + DBUG_RETURN(-1); + if (conds) { Item::cond_result cond_value; @@ -321,7 +322,7 @@ int mysql_update(THD *thd, to update NOTE: filesort will call table->prepare_for_position() */ - uint length; + uint length= 0; SORT_FIELD *sortorder; ha_rows examined_rows; @@ -691,7 +692,7 @@ int mysql_update(THD *thd, thd->row_count_func= (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated; send_ok(thd, (ulong) thd->row_count_func, id, buff); - DBUG_PRINT("info",("%d records updated",updated)); + DBUG_PRINT("info",("%ld records updated", (long) updated)); } thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */ thd->abort_on_warning= 0; @@ -760,7 +761,7 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, /* Check that we are not using table that we are updating in a sub select */ { TABLE_LIST *duplicate; - if ((duplicate= unique_table(thd, table_list, table_list->next_global))) + if ((duplicate= unique_table(thd, table_list, table_list->next_global, 0))) { update_non_unique_table_error(table_list, "UPDATE", duplicate); my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name); @@ -788,7 +789,7 @@ static table_map get_table_map(List<Item> *items) while ((item= (Item_field *) item_it++)) map|= item->used_tables(); - DBUG_PRINT("info",("table_map: 0x%08x", map)); + DBUG_PRINT("info", ("table_map: 0x%08lx", (long) map)); return map; } @@ -906,7 +907,7 @@ reopen_tables: tl->lock_type= using_update_log ? TL_READ_NO_INSERT : TL_READ; tl->updating= 0; /* Update TABLE::lock_type accordingly. */ - if (!tl->placeholder() && !tl->schema_table && !using_lock_tables) + if (!tl->placeholder() && !using_lock_tables) tl->table->reginfo.lock_type= tl->lock_type; } } @@ -986,7 +987,7 @@ reopen_tables: tl->lock_type != TL_READ_NO_INSERT) { TABLE_LIST *duplicate; - if ((duplicate= unique_table(thd, tl, table_list))) + if ((duplicate= unique_table(thd, tl, table_list, 0))) { update_non_unique_table_error(table_list, "UPDATE", duplicate); DBUG_RETURN(TRUE); @@ -1161,27 +1162,71 @@ int multi_update::prepare(List<Item> ¬_used_values, for (i=0 ; i < table_count ; i++) set_if_bigger(max_fields, fields_for_table[i]->elements); copy_field= new Copy_field[max_fields]; + DBUG_RETURN(thd->is_fatal_error != 0); +} - /* - Mark all copies of tables that are updates to ensure that - init_read_record() will not try to enable a cache on them - The problem is that for queries like +/* + Check if table is safe to update on fly - UPDATE t1, t1 AS t2 SET t1.b=t2.c WHERE t1.a=t2.a; + SYNOPSIS + safe_update_on_fly() + thd Thread handler + join_tab How table is used in join + all_tables List of tables - the row buffer may contain things that doesn't match what is on disk - which will cause an error when reading a row. - (This issue is mostly relevent for MyISAM tables) - */ - for (table_ref= leaves; table_ref; table_ref= table_ref->next_leaf) - { - TABLE *table=table_ref->table; - if ((tables_to_update & table->map) && - unique_table(thd, table_ref, update_tables)) - table->no_cache= 1; // Disable row cache + NOTES + We can update the first table in join on the fly if we know that + a row in this table will never be read twice. This is true under + the following conditions: + + - We are doing a table scan and the data is in a separate file (MyISAM) or + if we don't update a clustered key. + + - We are doing a range scan and we don't update the scan key or + the primary key for a clustered table handler. + + - Table is not joined to itself. + + This function gets information about fields to be updated from + the TABLE::write_set bitmap. + + WARNING + This code is a bit dependent of how make_join_readinfo() works. + + RETURN + 0 Not safe to update + 1 Safe to update +*/ + +static bool safe_update_on_fly(THD *thd, JOIN_TAB *join_tab, + TABLE_LIST *table_ref, TABLE_LIST *all_tables) +{ + TABLE *table= join_tab->table; + if (unique_table(thd, table_ref, all_tables, 0)) + return 0; + switch (join_tab->type) { + case JT_SYSTEM: + case JT_CONST: + case JT_EQ_REF: + return TRUE; // At most one matching row + case JT_REF: + case JT_REF_OR_NULL: + return !is_key_used(table, join_tab->ref.key, table->write_set); + case JT_ALL: + /* If range search on index */ + if (join_tab->quick) + return !join_tab->quick->is_keys_used(table->write_set); + /* If scanning in clustered key */ + if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) && + table->s->primary_key < MAX_KEY) + return !is_key_used(table, table->s->primary_key, table->write_set); + return TRUE; + default: + break; // Avoid compler warning } - DBUG_RETURN(thd->is_fatal_error != 0); + return FALSE; + } @@ -1221,7 +1266,7 @@ multi_update::initialize_tables(JOIN *join) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); if (table == main_table) // First table in join { - if (safe_update_on_fly(join->join_tab)) + if (safe_update_on_fly(thd, join->join_tab, table_ref, all_tables)) { table_to_update= main_table; // Update table on the fly continue; @@ -1273,61 +1318,6 @@ multi_update::initialize_tables(JOIN *join) DBUG_RETURN(0); } -/* - Check if table is safe to update on fly - - SYNOPSIS - safe_update_on_fly - join_tab How table is used in join - - NOTES - We can update the first table in join on the fly if we know that - a row in this table will never be read twice. This is true under - the following conditions: - - - We are doing a table scan and the data is in a separate file (MyISAM) or - if we don't update a clustered key. - - - We are doing a range scan and we don't update the scan key or - the primary key for a clustered table handler. - - This function gets information about fields to be updated from - the TABLE::write_set bitmap. - - WARNING - This code is a bit dependent of how make_join_readinfo() works. - - RETURN - 0 Not safe to update - 1 Safe to update -*/ - -static bool safe_update_on_fly(JOIN_TAB *join_tab) -{ - TABLE *table= join_tab->table; - switch (join_tab->type) { - case JT_SYSTEM: - case JT_CONST: - case JT_EQ_REF: - return TRUE; // At most one matching row - case JT_REF: - case JT_REF_OR_NULL: - return !is_key_used(table, join_tab->ref.key, table->write_set); - case JT_ALL: - /* If range search on index */ - if (join_tab->quick) - return !join_tab->quick->is_keys_used(table->write_set); - /* If scanning in clustered key */ - if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) && - table->s->primary_key < MAX_KEY) - return !is_key_used(table, table->s->primary_key, table->write_set); - return TRUE; - default: - break; // Avoid compler warning - } - return FALSE; -} - multi_update::~multi_update() { @@ -1584,6 +1574,15 @@ int multi_update::do_updates(bool from_send_error) if (!can_compare_record || compare_record(table)) { + int error; + if ((error= cur_table->view_check_option(thd, ignore)) != + VIEW_CHECK_OK) + { + if (error == VIEW_CHECK_SKIP) + continue; + else if (error == VIEW_CHECK_ERROR) + goto err; + } if ((local_error=table->file->ha_update_row(table->record[1], table->record[0]))) { diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 32aba5ab74f..622f7b99d33 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -197,7 +196,7 @@ fill_defined_view_parts (THD *thd, TABLE_LIST *view) lex->definer= &view->definer; } if (lex->create_view_algorithm == VIEW_ALGORITHM_UNDEFINED) - lex->create_view_algorithm= decoy.algorithm; + lex->create_view_algorithm= (uint8) decoy.algorithm; if (lex->create_view_suid == VIEW_SUID_DEFAULT) lex->create_view_suid= decoy.view_suid ? VIEW_SUID_DEFINER : VIEW_SUID_INVOKER; @@ -212,6 +211,7 @@ fill_defined_view_parts (THD *thd, TABLE_LIST *view) SYNOPSIS mysql_create_view() thd - thread handler + views - views to create mode - VIEW_CREATE_NEW, VIEW_ALTER, VIEW_CREATE_OR_REPLACE RETURN VALUE @@ -219,7 +219,7 @@ fill_defined_view_parts (THD *thd, TABLE_LIST *view) TRUE Error */ -bool mysql_create_view(THD *thd, +bool mysql_create_view(THD *thd, TABLE_LIST *views, enum_view_create_mode mode) { LEX *lex= thd->lex; @@ -236,25 +236,9 @@ bool mysql_create_view(THD *thd, bool res= FALSE; DBUG_ENTER("mysql_create_view"); - if (lex->proc_list.first || - lex->result) - { - my_error(ER_VIEW_SELECT_CLAUSE, MYF(0), (lex->result ? - "INTO" : - "PROCEDURE")); - res= TRUE; - goto err; - } - if (lex->derived_tables || - lex->variables_used || lex->param_list.elements) - { - int err= (lex->derived_tables ? - ER_VIEW_SELECT_DERIVED : - ER_VIEW_SELECT_VARIABLE); - my_message(err, ER(err), MYF(0)); - res= TRUE; - goto err; - } + /* This is ensured in the parser. */ + DBUG_ASSERT(!lex->proc_list.first && !lex->result && + !lex->param_list.elements && !lex->derived_tables); if (mode != VIEW_CREATE_NEW) { @@ -548,6 +532,49 @@ bool mysql_create_view(THD *thd, } VOID(pthread_mutex_lock(&LOCK_open)); res= mysql_register_view(thd, view, mode); + + if (mysql_bin_log.is_open()) + { + String buff; + const LEX_STRING command[3]= + {{ C_STRING_WITH_LEN("CREATE ") }, + { C_STRING_WITH_LEN("ALTER ") }, + { C_STRING_WITH_LEN("CREATE OR REPLACE ") }}; + + buff.append(command[thd->lex->create_view_mode].str, + command[thd->lex->create_view_mode].length); + view_store_options(thd, views, &buff); + buff.append(STRING_WITH_LEN("VIEW ")); + /* Test if user supplied a db (ie: we did not use thd->db) */ + if (views->db && views->db[0] && + (thd->db == NULL || strcmp(views->db, thd->db))) + { + append_identifier(thd, &buff, views->db, + views->db_length); + buff.append('.'); + } + append_identifier(thd, &buff, views->table_name, + views->table_name_length); + if (lex->view_list.elements) + { + List_iterator_fast<LEX_STRING> names(lex->view_list); + LEX_STRING *name; + int i; + + for (i= 0; (name= names++); i++) + { + buff.append(i ? ", " : "("); + append_identifier(thd, &buff, name->str, name->length); + } + buff.append(')'); + } + buff.append(STRING_WITH_LEN(" AS ")); + buff.append(views->source.str, views->source.length); + + thd->binlog_query(THD::STMT_QUERY_TYPE, + buff.ptr(), buff.length(), FALSE, FALSE); + } + VOID(pthread_mutex_unlock(&LOCK_open)); if (view->revision != 1) query_cache_invalidate3(thd, view, 0); @@ -582,40 +609,40 @@ static const int num_view_backups= 3; */ static File_option view_parameters[]= {{{ C_STRING_WITH_LEN("query")}, - offsetof(TABLE_LIST, query), + my_offsetof(TABLE_LIST, query), FILE_OPTIONS_ESTRING}, {{ C_STRING_WITH_LEN("md5")}, - offsetof(TABLE_LIST, md5), + my_offsetof(TABLE_LIST, md5), FILE_OPTIONS_STRING}, {{ C_STRING_WITH_LEN("updatable")}, - offsetof(TABLE_LIST, updatable_view), + my_offsetof(TABLE_LIST, updatable_view), FILE_OPTIONS_ULONGLONG}, {{ C_STRING_WITH_LEN("algorithm")}, - offsetof(TABLE_LIST, algorithm), + my_offsetof(TABLE_LIST, algorithm), FILE_OPTIONS_ULONGLONG}, {{ C_STRING_WITH_LEN("definer_user")}, - offsetof(TABLE_LIST, definer.user), + my_offsetof(TABLE_LIST, definer.user), FILE_OPTIONS_STRING}, {{ C_STRING_WITH_LEN("definer_host")}, - offsetof(TABLE_LIST, definer.host), + my_offsetof(TABLE_LIST, definer.host), FILE_OPTIONS_STRING}, {{ C_STRING_WITH_LEN("suid")}, - offsetof(TABLE_LIST, view_suid), + my_offsetof(TABLE_LIST, view_suid), FILE_OPTIONS_ULONGLONG}, {{ C_STRING_WITH_LEN("with_check_option")}, - offsetof(TABLE_LIST, with_check), + my_offsetof(TABLE_LIST, with_check), FILE_OPTIONS_ULONGLONG}, {{ C_STRING_WITH_LEN("revision")}, - offsetof(TABLE_LIST, revision), + my_offsetof(TABLE_LIST, revision), FILE_OPTIONS_REV}, {{ C_STRING_WITH_LEN("timestamp")}, - offsetof(TABLE_LIST, timestamp), + my_offsetof(TABLE_LIST, timestamp), FILE_OPTIONS_TIMESTAMP}, {{ C_STRING_WITH_LEN("create-version")}, - offsetof(TABLE_LIST, file_version), + my_offsetof(TABLE_LIST, file_version), FILE_OPTIONS_ULONGLONG}, {{ C_STRING_WITH_LEN("source")}, - offsetof(TABLE_LIST, source), + my_offsetof(TABLE_LIST, source), FILE_OPTIONS_ESTRING}, {{NullS, 0}, 0, FILE_OPTIONS_STRING} @@ -647,7 +674,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, String str(buff,(uint32) sizeof(buff), system_charset_info); char md5[MD5_BUFF_LENGTH]; bool can_be_merged; - char dir_buff[FN_REFLEN], file_buff[FN_REFLEN], path_buff[FN_REFLEN]; + char dir_buff[FN_REFLEN], path_buff[FN_REFLEN]; const uchar *endp; LEX_STRING dir, file, path; DBUG_ENTER("mysql_register_view"); @@ -686,7 +713,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, File_parser *parser; path.str= path_buff; - fn_format(path_buff, file.str, dir.str, 0, MY_UNPACK_FILENAME); + fn_format(path_buff, file.str, dir.str, "", MY_UNPACK_FILENAME); path.length= strlen(path_buff); if (!access(path.str, F_OK)) @@ -835,7 +862,8 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, Query_arena *arena, backup; TABLE_LIST *top_view= table->top_table(); int res; - bool result; + bool result, view_is_mergeable; + TABLE_LIST *view_main_select_tables; DBUG_ENTER("mysql_make_view"); DBUG_PRINT("info", ("table: 0x%lx (%s)", (ulong) table, table->table_name)); @@ -1062,17 +1090,16 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, table->next_global= view_tables; } -#ifdef HAVE_ROW_BASED_REPLICATION /* If the view's body needs row-based binlogging (e.g. the VIEW is created from SELECT UUID()), the top statement also needs it. */ if (lex->binlog_row_based_if_mixed) old_lex->binlog_row_based_if_mixed= TRUE; -#endif - bool view_is_mergeable= (table->algorithm != VIEW_ALGORITHM_TMPTABLE && - lex->can_be_merged()); - TABLE_LIST *view_main_select_tables; + view_is_mergeable= (table->algorithm != VIEW_ALGORITHM_TMPTABLE && + lex->can_be_merged()); + LINT_INIT(view_main_select_tables); + if (view_is_mergeable) { /* @@ -1117,13 +1144,17 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, /* Prepare a security context to check underlying objects of the view */ - Security_context *save_security_ctx= thd->security_ctx; if (!(table->view_sctx= (Security_context *) thd->stmt_arena->alloc(sizeof(Security_context)))) goto err; /* Assign the context to the tables referenced in the view */ - for (tbl= view_tables; tbl; tbl= tbl->next_global) - tbl->security_ctx= table->view_sctx; + if (view_tables) + { + DBUG_ASSERT(view_tables_tail); + for (tbl= view_tables; tbl != view_tables_tail->next_global; + tbl= tbl->next_global) + tbl->security_ctx= table->view_sctx; + } /* assign security context to SELECT name resolution contexts of view */ for(SELECT_LEX *sl= lex->all_selects_list; sl; @@ -1241,6 +1272,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, but it will not be included to SELECT_LEX tree, because it will not be executed */ + table->select_lex->order_list.push_back(&lex->select_lex.order_list); goto ok; } @@ -1311,20 +1343,19 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) { char path[FN_REFLEN]; TABLE_LIST *view; - frm_type_enum type; String non_existant_views; char *wrong_object_db= NULL, *wrong_object_name= NULL; bool error= FALSE; enum legacy_db_type not_used; DBUG_ENTER("mysql_drop_view"); + VOID(pthread_mutex_lock(&LOCK_open)); for (view= views; view; view= view->next_local) { TABLE_SHARE *share; frm_type_enum type= FRMTYPE_ERROR; build_table_filename(path, sizeof(path), view->db, view->table_name, reg_ext, 0); - VOID(pthread_mutex_lock(&LOCK_open)); if (access(path, F_OK) || FRMTYPE_VIEW != (type= mysql_frm_type(thd, path, ¬_used))) @@ -1336,7 +1367,6 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), name); - VOID(pthread_mutex_unlock(&LOCK_open)); continue; } if (type == FRMTYPE_TABLE) @@ -1353,7 +1383,6 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) non_existant_views.append(','); non_existant_views.append(String(view->table_name,system_charset_info)); } - VOID(pthread_mutex_unlock(&LOCK_open)); continue; } if (my_delete(path, MYF(MY_WME))) @@ -1374,24 +1403,36 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) } query_cache_invalidate3(thd, view, 0); sp_cache_invalidate(); - VOID(pthread_mutex_unlock(&LOCK_open)); } + if (error) { + VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_RETURN(TRUE); } if (wrong_object_name) { + VOID(pthread_mutex_unlock(&LOCK_open)); my_error(ER_WRONG_OBJECT, MYF(0), wrong_object_db, wrong_object_name, "VIEW"); DBUG_RETURN(TRUE); } if (non_existant_views.length()) { + VOID(pthread_mutex_unlock(&LOCK_open)); my_error(ER_BAD_TABLE_ERROR, MYF(0), non_existant_views.c_ptr()); DBUG_RETURN(TRUE); } + + if (mysql_bin_log.is_open()) + { + thd->clear_error(); + thd->binlog_query(THD::STMT_QUERY_TYPE, + thd->query, thd->query_length, FALSE, FALSE); + } + send_ok(thd); + VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_RETURN(FALSE); } @@ -1469,7 +1510,6 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view) TABLE *table; Field_translator *trans, *end_of_trans; KEY *key_info, *key_info_end; - uint i; DBUG_ENTER("check_key_in_view"); /* diff --git a/sql/sql_view.h b/sql/sql_view.h index 7e54d57cfdf..d3c83c82f44 100644 --- a/sql/sql_view.h +++ b/sql/sql_view.h @@ -3,8 +3,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -16,7 +15,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -bool mysql_create_view(THD *thd, +bool mysql_create_view(THD *thd, TABLE_LIST *view, enum_view_create_mode mode); bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 26dea00029a..5d24fb4fa65 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -48,21 +47,42 @@ const LEX_STRING null_lex_str={0,0}; #define yyoverflow(A,B,C,D,E,F) {ulong val= *(F); if (my_yyoverflow((B), (D), &val)) { yyerror((char*) (A)); return 2; } else { *(F)= (YYSIZE_T)val; }} -#define YYERROR_UNLESS(A) \ +#define MYSQL_YYABORT \ + do \ + { \ + LEX::cleanup_lex_after_parse_error(YYTHD);\ + YYABORT; \ + } while (0) + +#define MYSQL_YYABORT_UNLESS(A) \ if (!(A)) \ - { \ - yyerror(ER(ER_SYNTAX_ERROR)); \ - YYABORT; \ + { \ + my_parse_error(ER(ER_SYNTAX_ERROR));\ + MYSQL_YYABORT; \ } -/* Helper for parsing "IS [NOT] truth_value" */ -inline Item *is_truth_value(Item *A, bool v1, bool v2) -{ - return new Item_func_if(create_func_ifnull(A, - new Item_int((char *) (v2 ? "TRUE" : "FALSE"), v2, 1)), - new Item_int((char *) (v1 ? "TRUE" : "FALSE"), v1, 1), - new Item_int((char *) (v1 ? "FALSE" : "TRUE"),!v1, 1)); -} +/* + Work around for broken code generated by bison 1.875. + + The code generated by bison 1.875a and later, bison 2.1 and bison 2.2 is ok. + With bison 1.875 however, the generated code contains: +<pre> + yyerrlab1: + #if defined (__GNUC_MINOR__) && 2093 <= (__GNUC__ * 1000 + __GNUC_MINOR__) + __attribute__ ((__unused__)) + #endif +</pre> + This usage of __attribute__ is illegal, so we remove it. + See the following references for details: + http://lists.gnu.org/archive/html/bug-bison/2004-02/msg00014.html + http://gcc.gnu.org/bugzilla/show_bug.cgi?id=14273 +*/ + +#if defined (__GNUC_MINOR__) && 2093 <= (__GNUC__ * 1000 + __GNUC_MINOR__) +#undef __attribute__ +#define __attribute__(X) +#endif + #ifndef DBUG_OFF #define YYDEBUG 1 @@ -70,6 +90,66 @@ inline Item *is_truth_value(Item *A, bool v1, bool v2) #define YYDEBUG 0 #endif +/** + @brief Push an error message into MySQL error stack with line + and position information. + + This function provides semantic action implementers with a way + to push the famous "You have a syntax error near..." error + message into the error stack, which is normally produced only if + a parse error is discovered internally by the Bison generated + parser. +*/ + +void my_parse_error(const char *s) +{ + THD *thd= current_thd; + + char *yytext= (char*) thd->lex->tok_start; + /* Push an error into the error stack */ + my_printf_error(ER_PARSE_ERROR, ER(ER_PARSE_ERROR), MYF(0), s, + (yytext ? (char*) yytext : ""), + thd->lex->yylineno); +} + +/** + @brief Bison callback to report a syntax/OOM error + + This function is invoked by the bison-generated parser + when a syntax error, a parse error or an out-of-memory + condition occurs. This function is not invoked when the + parser is requested to abort by semantic action code + by means of YYABORT or YYACCEPT macros. This is why these + macros should not be used (use MYSQL_YYABORT/MYSQL_YYACCEPT + instead). + + The parser will abort immediately after invoking this callback. + + This function is not for use in semantic actions and is internal to + the parser, as it performs some pre-return cleanup. + In semantic actions, please use my_parse_error or my_error to + push an error into the error stack and MYSQL_YYABORT + to abort from the parser. +*/ + +void MYSQLerror(const char *s) +{ + THD *thd= current_thd; + + /* + Restore the original LEX if it was replaced when parsing + a stored procedure. We must ensure that a parsing error + does not leave any side effects in the THD. + */ + LEX::cleanup_lex_after_parse_error(thd); + + /* "parse error" changed into "syntax error" between bison 1.75 and 1.875 */ + if (strcmp(s,"parse error") == 0 || strcmp(s,"syntax error") == 0) + s= ER(ER_SYNTAX_ERROR); + my_parse_error(s); +} + + #ifndef DBUG_OFF void turn_parser_debug_on() { @@ -92,6 +172,273 @@ void turn_parser_debug_on() } #endif +static bool is_native_function(THD *thd, const LEX_STRING *name) +{ + if (find_native_function_builder(thd, *name)) + return true; + + if (is_lex_native_function(name)) + return true; + + return false; +} + + +/** + Helper action for a case statement (entering the CASE). + This helper is used for both 'simple' and 'searched' cases. + This helper, with the other case_stmt_action_..., is executed when + the following SQL code is parsed: +<pre> +CREATE PROCEDURE proc_19194_simple(i int) +BEGIN + DECLARE str CHAR(10); + + CASE i + WHEN 1 THEN SET str="1"; + WHEN 2 THEN SET str="2"; + WHEN 3 THEN SET str="3"; + ELSE SET str="unknown"; + END CASE; + + SELECT str; +END +</pre> + The actions are used to generate the following code: +<pre> +SHOW PROCEDURE CODE proc_19194_simple; +Pos Instruction +0 set str@1 NULL +1 set_case_expr (12) 0 i@0 +2 jump_if_not 5(12) (case_expr@0 = 1) +3 set str@1 _latin1'1' +4 jump 12 +5 jump_if_not 8(12) (case_expr@0 = 2) +6 set str@1 _latin1'2' +7 jump 12 +8 jump_if_not 11(12) (case_expr@0 = 3) +9 set str@1 _latin1'3' +10 jump 12 +11 set str@1 _latin1'unknown' +12 stmt 0 "SELECT str" +</pre> + + @param lex the parser lex context +*/ + +void case_stmt_action_case(LEX *lex) +{ + lex->sphead->new_cont_backpatch(NULL); + + /* + BACKPATCH: Creating target label for the jump to + "case_stmt_action_end_case" + (Instruction 12 in the example) + */ + + lex->spcont->push_label((char *)"", lex->sphead->instructions()); +} + +/** + Helper action for a case expression statement (the expr in 'CASE expr'). + This helper is used for 'searched' cases only. + @param lex the parser lex context + @param expr the parsed expression + @return 0 on success +*/ + +int case_stmt_action_expr(LEX *lex, Item* expr) +{ + sp_head *sp= lex->sphead; + sp_pcontext *parsing_ctx= lex->spcont; + int case_expr_id= parsing_ctx->register_case_expr(); + sp_instr_set_case_expr *i; + + if (parsing_ctx->push_case_expr_id(case_expr_id)) + return 1; + + i= new sp_instr_set_case_expr(sp->instructions(), + parsing_ctx, case_expr_id, expr, lex); + + sp->add_cont_backpatch(i); + sp->add_instr(i); + + return 0; +} + +/** + Helper action for a case when condition. + This helper is used for both 'simple' and 'searched' cases. + @param lex the parser lex context + @param when the parsed expression for the WHEN clause + @param simple true for simple cases, false for searched cases +*/ + +void case_stmt_action_when(LEX *lex, Item *when, bool simple) +{ + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + uint ip= sp->instructions(); + sp_instr_jump_if_not *i; + Item_case_expr *var; + Item *expr; + + if (simple) + { + var= new Item_case_expr(ctx->get_current_case_expr_id()); + +#ifndef DBUG_OFF + if (var) + { + var->m_sp= sp; + } +#endif + + expr= new Item_func_eq(var, when); + i= new sp_instr_jump_if_not(ip, ctx, expr, lex); + } + else + i= new sp_instr_jump_if_not(ip, ctx, when, lex); + + /* + BACKPATCH: Registering forward jump from + "case_stmt_action_when" to "case_stmt_action_then" + (jump_if_not from instruction 2 to 5, 5 to 8 ... in the example) + */ + + sp->push_backpatch(i, ctx->push_label((char *)"", 0)); + sp->add_cont_backpatch(i); + sp->add_instr(i); +} + +/** + Helper action for a case then statements. + This helper is used for both 'simple' and 'searched' cases. + @param lex the parser lex context +*/ + +void case_stmt_action_then(LEX *lex) +{ + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + uint ip= sp->instructions(); + sp_instr_jump *i = new sp_instr_jump(ip, ctx); + sp->add_instr(i); + + /* + BACKPATCH: Resolving forward jump from + "case_stmt_action_when" to "case_stmt_action_then" + (jump_if_not from instruction 2 to 5, 5 to 8 ... in the example) + */ + + sp->backpatch(ctx->pop_label()); + + /* + BACKPATCH: Registering forward jump from + "case_stmt_action_then" to "case_stmt_action_end_case" + (jump from instruction 4 to 12, 7 to 12 ... in the example) + */ + + sp->push_backpatch(i, ctx->last_label()); +} + +/** + Helper action for an end case. + This helper is used for both 'simple' and 'searched' cases. + @param lex the parser lex context + @param simple true for simple cases, false for searched cases +*/ + +void case_stmt_action_end_case(LEX *lex, bool simple) +{ + /* + BACKPATCH: Resolving forward jump from + "case_stmt_action_then" to "case_stmt_action_end_case" + (jump from instruction 4 to 12, 7 to 12 ... in the example) + */ + lex->sphead->backpatch(lex->spcont->pop_label()); + + if (simple) + lex->spcont->pop_case_expr_id(); + + lex->sphead->do_cont_backpatch(); +} + +/** + Helper to resolve the SQL:2003 Syntax exception 1) in <in predicate>. + See SQL:2003, Part 2, section 8.4 <in predicate>, Note 184, page 383. + This function returns the proper item for the SQL expression + <code>left [NOT] IN ( expr )</code> + @param thd the current thread + @param left the in predicand + @param equal true for IN predicates, false for NOT IN predicates + @param expr first and only expression of the in value list + @return an expression representing the IN predicate. +*/ +Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal, + Item *expr) +{ + /* + Relevant references for this issue: + - SQL:2003, Part 2, section 8.4 <in predicate>, page 383, + - SQL:2003, Part 2, section 7.2 <row value expression>, page 296, + - SQL:2003, Part 2, section 6.3 <value expression primary>, page 174, + - SQL:2003, Part 2, section 7.15 <subquery>, page 370, + - SQL:2003 Feature F561, "Full value expressions". + + The exception in SQL:2003 Note 184 means: + Item_singlerow_subselect, which corresponds to a <scalar subquery>, + should be re-interpreted as an Item_in_subselect, which corresponds + to a <table subquery> when used inside an <in predicate>. + + Our reading of Note 184 is reccursive, so that all: + - IN (( <subquery> )) + - IN ((( <subquery> ))) + - IN '('^N <subquery> ')'^N + - etc + should be interpreted as a <table subquery>, no matter how deep in the + expression the <subquery> is. + */ + + Item *result; + + DBUG_ENTER("handle_sql2003_note184_exception"); + + if (expr->type() == Item::SUBSELECT_ITEM) + { + Item_subselect *expr2 = (Item_subselect*) expr; + + if (expr2->substype() == Item_subselect::SINGLEROW_SUBS) + { + Item_singlerow_subselect *expr3 = (Item_singlerow_subselect*) expr2; + st_select_lex *subselect; + + /* + Implement the mandated change, by altering the semantic tree: + left IN Item_singlerow_subselect(subselect) + is modified to + left IN (subselect) + which is represented as + Item_in_subselect(left, subselect) + */ + subselect= expr3->invalidate_and_restore_select_lex(); + result= new (thd->mem_root) Item_in_subselect(left, subselect); + + if (! equal) + result = negate_expression(thd, result); + + DBUG_RETURN(result); + } + } + + if (equal) + result= new (thd->mem_root) Item_func_eq(left, expr); + else + result= new (thd->mem_root) Item_func_ne(left, expr); + + DBUG_RETURN(result); +} + %} %union { int num; @@ -141,222 +488,223 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %} %pure_parser /* We have threads */ +/* + Currently there is 287 shift/reduce conflict. We should not introduce + new conflicts any more. +*/ +%expect 287 -%token END_OF_INPUT +/* + Comments for TOKENS. + For each token, please include in the same line a comment that contains + the following tags: + SQL-2003-R : Reserved keyword as per SQL-2003 + SQL-2003-N : Non Reserved keyword as per SQL-2003 + SQL-1999-R : Reserved keyword as per SQL-1999 + SQL-1999-N : Non Reserved keyword as per SQL-1999 + MYSQL : MySQL extention (unspecified) + MYSQL-FUNC : MySQL extention, function + INTERNAL : Not a real token, lex optimization + OPERATOR : SQL operator + FUTURE-USE : Reserved for futur use + + This makes the code grep-able, and helps maintenance. +*/ -%token ABORT_SYM +%token ABORT_SYM /* INTERNAL (used in lex) */ %token ACCESSIBLE_SYM -%token ACTION -%token ADD -%token ADDDATE_SYM -%token AFTER_SYM +%token ACTION /* SQL-2003-N */ +%token ADD /* SQL-2003-R */ +%token ADDDATE_SYM /* MYSQL-FUNC */ +%token AFTER_SYM /* SQL-2003-N */ %token AGAINST %token AGGREGATE_SYM %token ALGORITHM_SYM -%token ALL -%token ALTER +%token ALL /* SQL-2003-R */ +%token ALTER /* SQL-2003-R */ %token ANALYZE_SYM -%token AND_AND_SYM -%token AND_SYM -%token ANY_SYM -%token AS -%token ASC -%token ASCII_SYM -%token ASENSITIVE_SYM -%token AT_SYM -%token ATAN +%token AND_AND_SYM /* OPERATOR */ +%token AND_SYM /* SQL-2003-R */ +%token ANY_SYM /* SQL-2003-R */ +%token AS /* SQL-2003-R */ +%token ASC /* SQL-2003-N */ +%token ASCII_SYM /* MYSQL-FUNC */ +%token ASENSITIVE_SYM /* FUTURE-USE */ +%token AT_SYM /* SQL-2003-R */ %token AUTHORS_SYM -%token AUTO_INC %token AUTOEXTEND_SIZE_SYM +%token AUTO_INC %token AVG_ROW_LENGTH -%token AVG_SYM +%token AVG_SYM /* SQL-2003-N */ %token BACKUP_SYM -%token BEFORE_SYM -%token BEGIN_SYM -%token BENCHMARK_SYM -%token BIGINT -%token BINARY +%token BEFORE_SYM /* SQL-2003-N */ +%token BEGIN_SYM /* SQL-2003-R */ +%token BETWEEN_SYM /* SQL-2003-R */ +%token BIGINT /* SQL-2003-R */ +%token BINARY /* SQL-2003-R */ %token BINLOG_SYM %token BIN_NUM -%token BIT_AND -%token BIT_OR -%token BIT_SYM -%token BIT_XOR -%token BLOB_SYM -%token BOOLEAN_SYM +%token BIT_AND /* MYSQL-FUNC */ +%token BIT_OR /* MYSQL-FUNC */ +%token BIT_SYM /* MYSQL-FUNC */ +%token BIT_XOR /* MYSQL-FUNC */ +%token BLOB_SYM /* SQL-2003-R */ +%token BOOLEAN_SYM /* SQL-2003-R */ %token BOOL_SYM -%token BOTH +%token BOTH /* SQL-2003-R */ %token BTREE_SYM -%token BY +%token BY /* SQL-2003-R */ %token BYTE_SYM %token CACHE_SYM -%token CALL_SYM -%token CASCADE -%token CASCADED -%token CAST_SYM -%token CHAIN_SYM +%token CALL_SYM /* SQL-2003-R */ +%token CASCADE /* SQL-2003-N */ +%token CASCADED /* SQL-2003-R */ +%token CASE_SYM /* SQL-2003-R */ +%token CAST_SYM /* SQL-2003-R */ +%token CHAIN_SYM /* SQL-2003-N */ %token CHANGE %token CHANGED %token CHARSET -%token CHAR_SYM +%token CHAR_SYM /* SQL-2003-R */ %token CHECKSUM_SYM -%token CHECK_SYM +%token CHECK_SYM /* SQL-2003-R */ %token CIPHER_SYM %token CLIENT_SYM -%token CLOSE_SYM -%token COALESCE +%token CLOSE_SYM /* SQL-2003-R */ +%token COALESCE /* SQL-2003-N */ %token CODE_SYM -%token COLLATE_SYM -%token COLLATION_SYM +%token COLLATE_SYM /* SQL-2003-R */ +%token COLLATION_SYM /* SQL-2003-N */ %token COLUMNS -%token COLUMN_SYM +%token COLUMN_SYM /* SQL-2003-R */ %token COMMENT_SYM -%token COMMITTED_SYM -%token COMMIT_SYM +%token COMMITTED_SYM /* SQL-2003-N */ +%token COMMIT_SYM /* SQL-2003-R */ %token COMPACT_SYM %token COMPLETION_SYM %token COMPRESSED_SYM -%token CONCAT -%token CONCAT_WS %token CONCURRENT -%token CONDITION_SYM +%token CONDITION_SYM /* SQL-2003-N */ %token CONNECTION_SYM %token CONSISTENT_SYM -%token CONSTRAINT -%token CONTAINS_SYM -%token CONTINUE_SYM +%token CONSTRAINT /* SQL-2003-R */ +%token CONTAINS_SYM /* SQL-2003-N */ +%token CONTINUE_SYM /* SQL-2003-R */ %token CONTRIBUTORS_SYM -%token CONVERT_SYM -%token CONVERT_TZ_SYM -%token COUNT_SYM -%token CREATE -%token CROSS -%token CUBE_SYM -%token CURDATE -%token CURRENT_USER -%token CURSOR_SYM -%token CURTIME +%token CONVERT_SYM /* SQL-2003-N */ +%token COUNT_SYM /* SQL-2003-N */ +%token CREATE /* SQL-2003-R */ +%token CROSS /* SQL-2003-R */ +%token CUBE_SYM /* SQL-2003-R */ +%token CURDATE /* MYSQL-FUNC */ +%token CURRENT_USER /* SQL-2003-R */ +%token CURSOR_SYM /* SQL-2003-R */ +%token CURTIME /* MYSQL-FUNC */ %token DATABASE %token DATABASES %token DATAFILE_SYM -%token DATA_SYM +%token DATA_SYM /* SQL-2003-N */ %token DATETIME -%token DATE_ADD_INTERVAL -%token DATE_SUB_INTERVAL -%token DATE_SYM +%token DATE_ADD_INTERVAL /* MYSQL-FUNC */ +%token DATE_SUB_INTERVAL /* MYSQL-FUNC */ +%token DATE_SYM /* SQL-2003-R */ %token DAY_HOUR_SYM %token DAY_MICROSECOND_SYM %token DAY_MINUTE_SYM %token DAY_SECOND_SYM -%token DAY_SYM -%token DEALLOCATE_SYM +%token DAY_SYM /* SQL-2003-R */ +%token DEALLOCATE_SYM /* SQL-2003-R */ %token DECIMAL_NUM -%token DECIMAL_SYM -%token DECLARE_SYM -%token DECODE_SYM -%token DEFAULT +%token DECIMAL_SYM /* SQL-2003-R */ +%token DECLARE_SYM /* SQL-2003-R */ +%token DEFAULT /* SQL-2003-R */ %token DEFINER_SYM %token DELAYED_SYM %token DELAY_KEY_WRITE_SYM -%token DELETE_SYM -%token DESC -%token DESCRIBE -%token DES_DECRYPT_SYM -%token DES_ENCRYPT_SYM +%token DELETE_SYM /* SQL-2003-R */ +%token DESC /* SQL-2003-N */ +%token DESCRIBE /* SQL-2003-R */ %token DES_KEY_FILE -%token DETERMINISTIC_SYM +%token DETERMINISTIC_SYM /* SQL-2003-R */ %token DIRECTORY_SYM %token DISABLE_SYM %token DISCARD %token DISK_SYM -%token DISTINCT +%token DISTINCT /* SQL-2003-R */ %token DIV_SYM -%token DOUBLE_SYM +%token DOUBLE_SYM /* SQL-2003-R */ %token DO_SYM -%token DROP +%token DROP /* SQL-2003-R */ %token DUAL_SYM %token DUMPFILE %token DUPLICATE_SYM -%token DYNAMIC_SYM -%token EACH_SYM +%token DYNAMIC_SYM /* SQL-2003-R */ +%token EACH_SYM /* SQL-2003-R */ +%token ELSE /* SQL-2003-R */ %token ELSEIF_SYM -%token ELT_FUNC %token ENABLE_SYM %token ENCLOSED -%token ENCODE_SYM -%token ENCRYPT -%token END +%token END /* SQL-2003-R */ %token ENDS_SYM +%token END_OF_INPUT /* INTERNAL */ %token ENGINES_SYM %token ENGINE_SYM %token ENUM -%token EQ -%token EQUAL_SYM +%token EQ /* OPERATOR */ +%token EQUAL_SYM /* OPERATOR */ %token ERRORS %token ESCAPED -%token ESCAPE_SYM -%token EVENT_SYM +%token ESCAPE_SYM /* SQL-2003-R */ %token EVENTS_SYM -%token EVERY_SYM -%token EXECUTE_SYM -%token EXISTS +%token EVENT_SYM +%token EVERY_SYM /* SQL-2003-N */ +%token EXECUTE_SYM /* SQL-2003-R */ +%token EXISTS /* SQL-2003-R */ %token EXIT_SYM %token EXPANSION_SYM -%token EXPORT_SET %token EXTENDED_SYM %token EXTENT_SIZE_SYM -%token EXTRACT_SYM -%token FALSE_SYM +%token EXTRACT_SYM /* SQL-2003-N */ +%token FALSE_SYM /* SQL-2003-R */ %token FAST_SYM -%token FETCH_SYM -%token FIELD_FUNC +%token FETCH_SYM /* SQL-2003-R */ %token FILE_SYM -%token FIRST_SYM +%token FIRST_SYM /* SQL-2003-N */ %token FIXED_SYM %token FLOAT_NUM -%token FLOAT_SYM +%token FLOAT_SYM /* SQL-2003-R */ %token FLUSH_SYM %token FORCE_SYM -%token FOREIGN -%token FORMAT_SYM -%token FOR_SYM -%token FOUND_SYM +%token FOREIGN /* SQL-2003-R */ +%token FOR_SYM /* SQL-2003-R */ +%token FOUND_SYM /* SQL-2003-R */ %token FRAC_SECOND_SYM %token FROM -%token FROM_UNIXTIME -%token FULL +%token FULL /* SQL-2003-R */ %token FULLTEXT_SYM -%token FUNCTION_SYM -%token FUNC_ARG0 -%token FUNC_ARG1 -%token FUNC_ARG2 -%token FUNC_ARG3 +%token FUNCTION_SYM /* SQL-2003-R */ %token GE -%token GEOMCOLLFROMTEXT %token GEOMETRYCOLLECTION %token GEOMETRY_SYM -%token GEOMFROMTEXT -%token GEOMFROMWKB -%token GET_FORMAT -%token GLOBAL_SYM -%token GRANT +%token GET_FORMAT /* MYSQL-FUNC */ +%token GLOBAL_SYM /* SQL-2003-R */ +%token GRANT /* SQL-2003-R */ %token GRANTS -%token GREATEST_SYM -%token GROUP +%token GROUP_SYM /* SQL-2003-R */ %token GROUP_CONCAT_SYM -%token GROUP_UNIQUE_USERS -%token GT_SYM +%token GT_SYM /* OPERATOR */ %token HANDLER_SYM %token HASH_SYM -%token HAVING +%token HAVING /* SQL-2003-R */ %token HELP_SYM %token HEX_NUM %token HIGH_PRIORITY +%token HOST_SYM %token HOSTS_SYM %token HOUR_MICROSECOND_SYM %token HOUR_MINUTE_SYM %token HOUR_SECOND_SYM -%token HOUR_SYM +%token HOUR_SYM /* SQL-2003-R */ %token IDENT %token IDENTIFIED_SYM %token IDENT_QUOTED @@ -367,70 +715,63 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token INDEX_SYM %token INFILE %token INITIAL_SIZE_SYM -%token INNER_SYM +%token INNER_SYM /* SQL-2003-R */ %token INNOBASE_SYM -%token INOUT_SYM -%token INSENSITIVE_SYM -%token INSERT +%token INOUT_SYM /* SQL-2003-R */ +%token INSENSITIVE_SYM /* SQL-2003-R */ +%token INSERT /* SQL-2003-R */ %token INSERT_METHOD %token INSTALL_SYM -%token INTERVAL_SYM -%token INTO -%token INT_SYM +%token INTERVAL_SYM /* SQL-2003-R */ +%token INTO /* SQL-2003-R */ +%token INT_SYM /* SQL-2003-R */ %token INVOKER_SYM -%token IN_SYM -%token IS -%token ISOLATION +%token IN_SYM /* SQL-2003-R */ +%token IS /* SQL-2003-R */ +%token ISOLATION /* SQL-2003-R */ %token ISSUER_SYM %token ITERATE_SYM -%token JOIN_SYM +%token JOIN_SYM /* SQL-2003-R */ %token KEYS -%token KEY_SYM %token KEY_BLOCK_SIZE +%token KEY_SYM /* SQL-2003-N */ %token KILL_SYM -%token LANGUAGE_SYM -%token LAST_INSERT_ID -%token LAST_SYM -%token LE -%token LEADING -%token LEAST_SYM +%token LANGUAGE_SYM /* SQL-2003-R */ +%token LAST_SYM /* SQL-2003-N */ +%token LE /* OPERATOR */ +%token LEADING /* SQL-2003-R */ %token LEAVES %token LEAVE_SYM -%token LEFT +%token LEFT /* SQL-2003-R */ %token LESS_SYM %token LEVEL_SYM %token LEX_HOSTNAME -%token LIKE +%token LIKE /* SQL-2003-R */ %token LIMIT %token LINEAR_SYM -%token LINEFROMTEXT %token LINES %token LINESTRING %token LIST_SYM %token LOAD -%token LOCAL_SYM -%token LOCATE -%token LOCATOR_SYM +%token LOCAL_SYM /* SQL-2003-R */ +%token LOCATOR_SYM /* SQL-2003-N */ %token LOCKS_SYM %token LOCK_SYM %token LOGFILE_SYM %token LOGS_SYM -%token LOG_SYM %token LONGBLOB %token LONGTEXT %token LONG_NUM %token LONG_SYM %token LOOP_SYM %token LOW_PRIORITY -%token LT -%token MAKE_SET_SYM +%token LT /* OPERATOR */ %token MASTER_CONNECT_RETRY_SYM %token MASTER_HOST_SYM %token MASTER_LOG_FILE_SYM %token MASTER_LOG_POS_SYM %token MASTER_PASSWORD_SYM %token MASTER_PORT_SYM -%token MASTER_POS_WAIT %token MASTER_SERVER_ID_SYM %token MASTER_SSL_CAPATH_SYM %token MASTER_SSL_CA_SYM @@ -440,134 +781,131 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token MASTER_SSL_SYM %token MASTER_SYM %token MASTER_USER_SYM -%token MATCH +%token MATCH /* SQL-2003-R */ %token MAX_CONNECTIONS_PER_HOUR %token MAX_QUERIES_PER_HOUR %token MAX_ROWS %token MAX_SIZE_SYM -%token MAX_SYM +%token MAX_SYM /* SQL-2003-N */ %token MAX_UPDATES_PER_HOUR %token MAX_USER_CONNECTIONS_SYM -%token MAX_VALUE_SYM +%token MAX_VALUE_SYM /* SQL-2003-N */ %token MEDIUMBLOB %token MEDIUMINT %token MEDIUMTEXT %token MEDIUM_SYM %token MEMORY_SYM -%token MERGE_SYM -%token MICROSECOND_SYM +%token MERGE_SYM /* SQL-2003-R */ +%token MICROSECOND_SYM /* MYSQL-FUNC */ %token MIGRATE_SYM %token MINUTE_MICROSECOND_SYM %token MINUTE_SECOND_SYM -%token MINUTE_SYM +%token MINUTE_SYM /* SQL-2003-R */ %token MIN_ROWS -%token MIN_SYM -%token MLINEFROMTEXT +%token MIN_SYM /* SQL-2003-N */ %token MODE_SYM -%token MODIFIES_SYM +%token MODIFIES_SYM /* SQL-2003-R */ %token MODIFY_SYM -%token MOD_SYM -%token MONTH_SYM -%token MPOINTFROMTEXT -%token MPOLYFROMTEXT +%token MOD_SYM /* SQL-2003-N */ +%token MONTH_SYM /* SQL-2003-R */ %token MULTILINESTRING %token MULTIPOINT %token MULTIPOLYGON %token MUTEX_SYM -%token NAMES_SYM -%token NAME_SYM -%token NATIONAL_SYM -%token NATURAL +%token NAMES_SYM /* SQL-2003-N */ +%token NAME_SYM /* SQL-2003-N */ +%token NATIONAL_SYM /* SQL-2003-R */ +%token NATURAL /* SQL-2003-R */ %token NCHAR_STRING -%token NCHAR_SYM +%token NCHAR_SYM /* SQL-2003-R */ %token NDBCLUSTER_SYM -%token NE -%token NEW_SYM -%token NEXT_SYM +%token NE /* OPERATOR */ +%token NEG +%token NEW_SYM /* SQL-2003-R */ +%token NEXT_SYM /* SQL-2003-N */ %token NODEGROUP_SYM -%token NONE_SYM +%token NONE_SYM /* SQL-2003-R */ %token NOT2_SYM -%token NOT_SYM +%token NOT_SYM /* SQL-2003-R */ %token NOW_SYM -%token NO_SYM +%token NO_SYM /* SQL-2003-R */ %token NO_WAIT_SYM %token NO_WRITE_TO_BINLOG -%token NULL_SYM +%token NULL_SYM /* SQL-2003-R */ %token NUM -%token NUMERIC_SYM +%token NUMERIC_SYM /* SQL-2003-R */ %token NVARCHAR_SYM %token OFFSET_SYM -%token OJ_SYM %token OLD_PASSWORD -%token ON +%token ON /* SQL-2003-R */ %token ONE_SHOT_SYM %token ONE_SYM -%token OPEN_SYM +%token OPEN_SYM /* SQL-2003-R */ %token OPTIMIZE -%token OPTION +%token OPTIONS_SYM +%token OPTION /* SQL-2003-N */ %token OPTIONALLY %token OR2_SYM -%token ORDER_SYM -%token OR_OR_SYM -%token OR_SYM +%token ORDER_SYM /* SQL-2003-R */ +%token OR_OR_SYM /* OPERATOR */ +%token OR_SYM /* SQL-2003-R */ %token OUTER %token OUTFILE -%token OUT_SYM +%token OUT_SYM /* SQL-2003-R */ +%token OWNER_SYM %token PACK_KEYS_SYM +%token PARAM_MARKER %token PARSER_SYM -%token PARTIAL -%token PARTITION_SYM +%token PARTIAL /* SQL-2003-N */ %token PARTITIONING_SYM %token PARTITIONS_SYM +%token PARTITION_SYM /* SQL-2003-R */ %token PASSWORD -%token PARAM_MARKER %token PHASE_SYM -%token PLUGIN_SYM %token PLUGINS_SYM -%token POINTFROMTEXT +%token PLUGIN_SYM %token POINT_SYM -%token POLYFROMTEXT %token POLYGON -%token POSITION_SYM -%token PRECISION -%token PREPARE_SYM +%token PORT_SYM +%token POSITION_SYM /* SQL-2003-N */ +%token PRECISION /* SQL-2003-R */ +%token PREPARE_SYM /* SQL-2003-R */ %token PRESERVE_SYM %token PREV_SYM -%token PRIMARY_SYM -%token PRIVILEGES -%token PROCEDURE +%token PRIMARY_SYM /* SQL-2003-R */ +%token PRIVILEGES /* SQL-2003-N */ +%token PROCEDURE /* SQL-2003-R */ %token PROCESS %token PROCESSLIST_SYM %token PURGE %token QUARTER_SYM %token QUERY_SYM %token QUICK -%token RAND -%token RANGE_SYM -%token READS_SYM +%token RANGE_SYM /* SQL-2003-R */ +%token READS_SYM /* SQL-2003-R */ %token READ_ONLY_SYM -%token READ_SYM +%token READ_SYM /* SQL-2003-N */ %token READ_WRITE_SYM -%token REAL +%token REAL /* SQL-2003-R */ %token REBUILD_SYM %token RECOVER_SYM -%token REDO_BUFFER_SIZE_SYM %token REDOFILE_SYM +%token REDO_BUFFER_SIZE_SYM %token REDUNDANT_SYM -%token REFERENCES +%token REFERENCES /* SQL-2003-R */ %token REGEXP %token RELAY_LOG_FILE_SYM %token RELAY_LOG_POS_SYM %token RELAY_THREAD -%token RELEASE_SYM +%token RELEASE_SYM /* SQL-2003-R */ %token RELOAD %token REMOVE_SYM %token RENAME %token REORGANIZE_SYM %token REPAIR -%token REPEATABLE_SYM -%token REPEAT_SYM -%token REPLACE +%token REPEATABLE_SYM /* SQL-2003-N */ +%token REPEAT_SYM /* MYSQL-FUNC */ +%token REPLACE /* MYSQL-FUNC */ %token REPLICATION %token REQUIRE_SYM %token RESET_SYM @@ -575,159 +913,158 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token RESTORE_SYM %token RESTRICT %token RESUME_SYM -%token RETURNS_SYM -%token RETURN_SYM -%token REVOKE -%token RIGHT -%token ROLLBACK_SYM -%token ROLLUP_SYM -%token ROUND -%token ROUTINE_SYM -%token ROWS_SYM -%token ROW_COUNT_SYM +%token RETURNS_SYM /* SQL-2003-R */ +%token RETURN_SYM /* SQL-2003-R */ +%token REVOKE /* SQL-2003-R */ +%token RIGHT /* SQL-2003-R */ +%token ROLLBACK_SYM /* SQL-2003-R */ +%token ROLLUP_SYM /* SQL-2003-R */ +%token ROUTINE_SYM /* SQL-2003-N */ +%token ROWS_SYM /* SQL-2003-R */ %token ROW_FORMAT_SYM -%token ROW_SYM +%token ROW_SYM /* SQL-2003-R */ %token RTREE_SYM -%token SAVEPOINT_SYM +%token SAVEPOINT_SYM /* SQL-2003-R */ %token SCHEDULE_SYM %token SECOND_MICROSECOND_SYM -%token SECOND_SYM -%token SECURITY_SYM -%token SELECT_SYM -%token SENSITIVE_SYM +%token SECOND_SYM /* SQL-2003-R */ +%token SECURITY_SYM /* SQL-2003-N */ +%token SELECT_SYM /* SQL-2003-R */ +%token SENSITIVE_SYM /* FUTURE-USE */ %token SEPARATOR_SYM -%token SERIALIZABLE_SYM +%token SERIALIZABLE_SYM /* SQL-2003-N */ %token SERIAL_SYM -%token SESSION_SYM -%token SET +%token SESSION_SYM /* SQL-2003-N */ +%token SERVER_SYM +%token SERVER_OPTIONS +%token SET /* SQL-2003-R */ %token SET_VAR %token SHARE_SYM -%token SHIFT_LEFT -%token SHIFT_RIGHT +%token SHIFT_LEFT /* OPERATOR */ +%token SHIFT_RIGHT /* OPERATOR */ %token SHOW %token SHUTDOWN %token SIGNED_SYM -%token SIMPLE_SYM +%token SIMPLE_SYM /* SQL-2003-N */ %token SLAVE -%token SMALLINT +%token SMALLINT /* SQL-2003-R */ %token SNAPSHOT_SYM +%token SOCKET_SYM %token SONAME_SYM %token SOUNDS_SYM %token SPATIAL_SYM -%token SPECIFIC_SYM -%token SQLEXCEPTION_SYM -%token SQLSTATE_SYM -%token SQLWARNING_SYM +%token SPECIFIC_SYM /* SQL-2003-R */ +%token SQLEXCEPTION_SYM /* SQL-2003-R */ +%token SQLSTATE_SYM /* SQL-2003-R */ +%token SQLWARNING_SYM /* SQL-2003-R */ %token SQL_BIG_RESULT %token SQL_BUFFER_RESULT %token SQL_CACHE_SYM %token SQL_CALC_FOUND_ROWS %token SQL_NO_CACHE_SYM %token SQL_SMALL_RESULT -%token SQL_SYM +%token SQL_SYM /* SQL-2003-R */ %token SQL_THREAD %token SSL_SYM %token STARTING -%token START_SYM %token STARTS_SYM +%token START_SYM /* SQL-2003-R */ %token STATUS_SYM +%token STDDEV_SAMP_SYM /* SQL-2003-N */ %token STD_SYM -%token STDDEV_SAMP_SYM %token STOP_SYM %token STORAGE_SYM %token STRAIGHT_JOIN %token STRING_SYM %token SUBDATE_SYM %token SUBJECT_SYM -%token SUBPARTITION_SYM %token SUBPARTITIONS_SYM -%token SUBSTRING -%token SUBSTRING_INDEX -%token SUM_SYM +%token SUBPARTITION_SYM +%token SUBSTRING /* SQL-2003-N */ +%token SUM_SYM /* SQL-2003-N */ %token SUPER_SYM %token SUSPEND_SYM %token SYSDATE %token TABLES %token TABLESPACE -%token TABLE_SYM -%token TEMPORARY +%token TABLE_REF_PRIORITY +%token TABLE_SYM /* SQL-2003-R */ +%token TEMPORARY /* SQL-2003-N */ %token TEMPTABLE_SYM %token TERMINATED %token TEXT_STRING %token TEXT_SYM -%token TIMESTAMP +%token THAN_SYM +%token THEN_SYM /* SQL-2003-R */ +%token TIMESTAMP /* SQL-2003-R */ %token TIMESTAMP_ADD %token TIMESTAMP_DIFF -%token TIME_SYM +%token TIME_SYM /* SQL-2003-R */ %token TINYBLOB %token TINYINT %token TINYTEXT -%token THAN_SYM -%token TO_SYM -%token TRAILING +%token TO_SYM /* SQL-2003-R */ +%token TRAILING /* SQL-2003-R */ %token TRANSACTION_SYM -%token TRIGGER_SYM %token TRIGGERS_SYM -%token TRIM -%token TRUE_SYM +%token TRIGGER_SYM /* SQL-2003-R */ +%token TRIM /* SQL-2003-N */ +%token TRUE_SYM /* SQL-2003-R */ %token TRUNCATE_SYM %token TYPES_SYM -%token TYPE_SYM +%token TYPE_SYM /* SQL-2003-N */ %token UDF_RETURNS_SYM %token ULONGLONG_NUM -%token UNCOMMITTED_SYM +%token UNCOMMITTED_SYM /* SQL-2003-N */ %token UNDEFINED_SYM -%token UNDO_BUFFER_SIZE_SYM -%token UNDOFILE_SYM %token UNDERSCORE_CHARSET -%token UNDO_SYM +%token UNDOFILE_SYM +%token UNDO_BUFFER_SIZE_SYM +%token UNDO_SYM /* FUTURE-USE */ %token UNICODE_SYM %token UNINSTALL_SYM -%token UNION_SYM +%token UNION_SYM /* SQL-2003-R */ %token UNIQUE_SYM -%token UNIQUE_USERS -%token UNIX_TIMESTAMP -%token UNKNOWN_SYM +%token UNKNOWN_SYM /* SQL-2003-R */ %token UNLOCK_SYM %token UNSIGNED %token UNTIL_SYM -%token UPDATE_SYM +%token UPDATE_SYM /* SQL-2003-R */ %token UPGRADE_SYM -%token USAGE -%token USER +%token USAGE /* SQL-2003-N */ +%token USER /* SQL-2003-R */ %token USE_FRM %token USE_SYM -%token USING +%token USING /* SQL-2003-R */ %token UTC_DATE_SYM %token UTC_TIMESTAMP_SYM %token UTC_TIME_SYM -%token VAR_SAMP_SYM -%token VALUES -%token VALUE_SYM +%token VALUES /* SQL-2003-R */ +%token VALUE_SYM /* SQL-2003-R */ %token VARBINARY -%token VARCHAR +%token VARCHAR /* SQL-2003-R */ %token VARIABLES %token VARIANCE_SYM -%token VARYING -%token VIEW_SYM +%token VARYING /* SQL-2003-R */ +%token VAR_SAMP_SYM +%token VIEW_SYM /* SQL-2003-N */ %token WAIT_SYM %token WARNINGS %token WEEK_SYM -%token WHEN_SYM -%token WHERE +%token WHEN_SYM /* SQL-2003-R */ +%token WHERE /* SQL-2003-R */ %token WHILE_SYM -%token WITH -%token WORK_SYM -%token WRITE_SYM +%token WITH /* SQL-2003-R */ +%token WORK_SYM /* SQL-2003-N */ +%token WRAPPER_SYM +%token WRITE_SYM /* SQL-2003-N */ %token X509_SYM %token XA_SYM %token XOR -%token YEARWEEK %token YEAR_MONTH_SYM -%token YEAR_SYM +%token YEAR_SYM /* SQL-2003-R */ %token ZEROFILL - %left JOIN_SYM INNER_SYM STRAIGHT_JOIN CROSS LEFT RIGHT /* A dummy token to force the priority of table_ref production in a join. */ %left TABLE_REF_PRIORITY @@ -751,7 +1088,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text UNDERSCORE_CHARSET IDENT_sys TEXT_STRING_sys TEXT_STRING_literal NCHAR_STRING opt_component key_cache_name - sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem + sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty %type <lex_str_ptr> opt_table_alias @@ -761,7 +1098,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <simple_string> remember_name remember_end opt_ident opt_db text_or_password - opt_constraint constraint ident_or_empty + opt_constraint constraint %type <string> text_string opt_gconcat_separator @@ -793,7 +1130,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <item> literal text_literal insert_ident order_ident simple_ident select_item2 expr opt_expr opt_else sum_expr in_sum_expr - bool_term bool_factor bool_test bool_pri + variable variable_aux bool_term bool_factor bool_test bool_pri predicate bit_expr bit_term bit_factor value_expr term factor table_wild simple_expr udf_expr expr_or_default set_expr_or_default interval_expr @@ -803,13 +1140,17 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); simple_ident_nospvar simple_ident_q field_or_var limit_option part_func_expr + function_call_keyword + function_call_nonkeyword + function_call_generic + function_call_conflict %type <item_num> NUM_literal %type <item_list> expr_list udf_expr_list udf_expr_list2 when_list - ident_list ident_list_arg + ident_list ident_list_arg opt_expr_list %type <var_type> option_type opt_var_type opt_var_ident_type @@ -836,7 +1177,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <interval_time_st> interval_time_st -%type <db_type> storage_engines +%type <db_type> storage_engines known_storage_engines %type <row_type> row_types @@ -848,7 +1189,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <udf_type> udf_func_type -%type <symbol> FUNC_ARG0 FUNC_ARG1 FUNC_ARG2 FUNC_ARG3 keyword keyword_sp +%type <symbol> keyword keyword_sp %type <lex_user> user grant_user @@ -860,6 +1201,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); old_or_new_charset_name_or_default collation_name collation_name_or_default + opt_load_data_charset %type <variable> internal_variable_name @@ -880,7 +1222,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); select_item_list select_item values_list no_braces opt_limit_clause delete_limit_clause fields opt_values values procedure_list procedure_list2 procedure_item - when_list2 expr_list2 udf_expr_list3 handler + expr_list2 udf_expr_list3 handler opt_precision opt_ignore opt_column opt_restrict grant revoke set lock unlock string_list field_options field_option field_opt_list opt_binary table_lock_list table_lock @@ -911,14 +1253,16 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); view_check_option trigger_tail sp_tail install uninstall partition_entry binlog_base64_event init_key_options key_options key_opts key_opt key_using_alg + server_def server_options_list server_option END_OF_INPUT %type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt %type <NONE> sp_proc_stmt_statement sp_proc_stmt_return -%type <NONE> sp_proc_stmt_if sp_proc_stmt_case_simple sp_proc_stmt_case +%type <NONE> sp_proc_stmt_if %type <NONE> sp_labeled_control sp_proc_stmt_unlabeled sp_proc_stmt_leave %type <NONE> sp_proc_stmt_iterate %type <NONE> sp_proc_stmt_open sp_proc_stmt_fetch sp_proc_stmt_close +%type <NONE> case_stmt_specification simple_case_stmt searched_case_stmt %type <num> sp_decl_idents sp_opt_inout sp_handler_type sp_hcond_list %type <spcondtype> sp_cond sp_hcond @@ -941,7 +1285,7 @@ query: (!(thd->lex->select_lex.options & OPTION_FOUND_COMMENT))) { my_message(ER_EMPTY_QUERY, ER(ER_EMPTY_QUERY), MYF(0)); - YYABORT; + MYSQL_YYABORT; } else { @@ -1013,12 +1357,12 @@ statement: deallocate: deallocate_or_drop PREPARE_SYM ident { - THD *thd=YYTHD; + THD *thd= YYTHD; LEX *lex= thd->lex; if (lex->stmt_prepare_mode) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } lex->sql_command= SQLCOM_DEALLOCATE_PREPARE; lex->prepared_stmt_name= $3; @@ -1033,12 +1377,12 @@ deallocate_or_drop: prepare: PREPARE_SYM ident FROM prepare_src { - THD *thd=YYTHD; + THD *thd= YYTHD; LEX *lex= thd->lex; if (lex->stmt_prepare_mode) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } lex->sql_command= SQLCOM_PREPARE; lex->prepared_stmt_name= $2; @@ -1047,14 +1391,14 @@ prepare: prepare_src: TEXT_STRING_sys { - THD *thd=YYTHD; + THD *thd= YYTHD; LEX *lex= thd->lex; lex->prepared_stmt_code= $1; lex->prepared_stmt_code_is_varref= FALSE; } | '@' ident_or_text { - THD *thd=YYTHD; + THD *thd= YYTHD; LEX *lex= thd->lex; lex->prepared_stmt_code= $2; lex->prepared_stmt_code_is_varref= TRUE; @@ -1063,12 +1407,12 @@ prepare_src: execute: EXECUTE_SYM ident { - THD *thd=YYTHD; + THD *thd= YYTHD; LEX *lex= thd->lex; if (lex->stmt_prepare_mode) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } lex->sql_command= SQLCOM_EXECUTE; lex->prepared_stmt_name= $2; @@ -1092,7 +1436,7 @@ execute_var_ident: '@' ident_or_text LEX *lex=Lex; LEX_STRING *lexstr= (LEX_STRING*)sql_memdup(&$2, sizeof(LEX_STRING)); if (!lexstr || lex->prepared_stmt_params.push_back(lexstr)) - YYABORT; + MYSQL_YYABORT; } ; @@ -1104,7 +1448,7 @@ help: if (Lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "HELP"); - YYABORT; + MYSQL_YYABORT; } } ident_or_text @@ -1224,14 +1568,14 @@ create: CREATE opt_table_options TABLE_SYM opt_if_not_exists table_ident { THD *thd= YYTHD; - LEX *lex=Lex; + LEX *lex= thd->lex; lex->sql_command= SQLCOM_CREATE_TABLE; if (!lex->select_lex.add_table_to_list(thd, $5, NULL, TL_OPTION_UPDATING, (using_update_log ? TL_READ_NO_INSERT: TL_READ))) - YYABORT; + MYSQL_YYABORT; lex->create_list.empty(); lex->key_list.empty(); lex->col_list.empty(); @@ -1240,7 +1584,8 @@ create: lex->create_info.options=$2 | $4; lex->create_info.db_type= lex->thd->variables.table_type; lex->create_info.default_table_charset= NULL; - lex->name= 0; + lex->name.str= 0; + lex->name.length= 0; lex->like_name= 0; } create2 @@ -1253,7 +1598,7 @@ create: if (!lex->current_select->add_table_to_list(lex->thd, $7, NULL, TL_OPTION_UPDATING)) - YYABORT; + MYSQL_YYABORT; lex->create_list.empty(); lex->key_list.empty(); lex->col_list.empty(); @@ -1264,8 +1609,8 @@ create: LEX *lex=Lex; if ($2 != Key::FULLTEXT && lex->key_create_info.parser_name.str) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } lex->key_list.push_back(new Key($2, $4.str, &lex->key_create_info, 0, lex->col_list)); @@ -1280,7 +1625,7 @@ create: { LEX *lex=Lex; lex->sql_command=SQLCOM_CREATE_DB; - lex->name=$4.str; + lex->name= $4; lex->create_info.options=$3; } | CREATE @@ -1295,7 +1640,7 @@ create: { Lex->sql_command = SQLCOM_CREATE_USER; } - | CREATE LOGFILE_SYM GROUP logfile_group_info + | CREATE LOGFILE_SYM GROUP_SYM logfile_group_info { Lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP; } @@ -1303,8 +1648,61 @@ create: { Lex->alter_tablespace_info->ts_cmd_type= CREATE_TABLESPACE; } + | CREATE server_def + { + Lex->sql_command= SQLCOM_CREATE_SERVER; + } ; +server_def: + SERVER_SYM ident_or_text FOREIGN DATA_SYM WRAPPER_SYM ident_or_text OPTIONS_SYM '(' server_options_list ')' + { + Lex->server_options.server_name= $2.str; + Lex->server_options.server_name_length= $2.length; + Lex->server_options.scheme= $6.str; + } + ; +server_options_list: + server_option + | server_options_list ',' server_option + ; + +server_option: + USER TEXT_STRING_sys + { + Lex->server_options.username= $2.str; + } + | + HOST_SYM TEXT_STRING_sys + { + Lex->server_options.host= $2.str; + } + | + DATABASE TEXT_STRING_sys + { + Lex->server_options.db= $2.str; + } + | + OWNER_SYM TEXT_STRING_sys + { + Lex->server_options.owner= $2.str; + } + | + PASSWORD TEXT_STRING_sys + { + Lex->server_options.password= $2.str; + } + | + SOCKET_SYM TEXT_STRING_sys + { + Lex->server_options.socket= $2.str; + } + | + PORT_SYM ulong_num + { + Lex->server_options.port= $2; + } + ; event_tail: EVENT_SYM opt_if_not_exists sp_name @@ -1316,7 +1714,7 @@ event_tail: Lex->create_info.options= $2; if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD))) - YYABORT; + MYSQL_YYABORT; Lex->event_parse_data->identifier= $3; /* @@ -1329,7 +1727,6 @@ event_tail: Lex->sql_command= SQLCOM_CREATE_EVENT; /* We need that for disallowing subqueries */ - Lex->expr_allows_subselect= FALSE; } ON SCHEDULE_SYM ev_schedule_time opt_ev_on_completion @@ -1351,7 +1748,6 @@ event_tail: can overwrite it */ Lex->sql_command= SQLCOM_CREATE_EVENT; - Lex->expr_allows_subselect= TRUE; } ; @@ -1421,6 +1817,7 @@ opt_ev_comment: /* empty */ { $$= 0; } | COMMENT_SYM TEXT_STRING_sys { Lex->comment= Lex->event_parse_data->comment= $2; + $$= 1; } ; @@ -1448,11 +1845,11 @@ ev_sql_stmt: if (lex->sphead) { my_error(ER_EVENT_RECURSIVITY_FORBIDDEN, MYF(0)); - YYABORT; + MYSQL_YYABORT; } if (!(lex->sphead= new sp_head())) - YYABORT; + MYSQL_YYABORT; lex->sphead->reset_thd_mem_root(YYTHD); lex->sphead->init(lex); @@ -1486,8 +1883,7 @@ ev_sql_stmt_inner: sp_proc_stmt_statement | sp_proc_stmt_return | sp_proc_stmt_if - | sp_proc_stmt_case_simple - | sp_proc_stmt_case + | case_stmt_specification | sp_labeled_control | sp_proc_stmt_unlabeled | sp_proc_stmt_leave @@ -1516,15 +1912,15 @@ clear_privileges: sp_name: ident '.' ident { - if (!$1.str || check_db_name($1.str)) + if (!$1.str || check_db_name(&$1)) { my_error(ER_WRONG_DB_NAME, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } if (check_routine_name($3)) { my_error(ER_SP_WRONG_NAME, MYF(0), $3.str); - YYABORT; + MYSQL_YYABORT; } $$= new sp_name($1, $3); $$->init_qname(YYTHD); @@ -1536,10 +1932,10 @@ sp_name: if (check_routine_name($1)) { my_error(ER_SP_WRONG_NAME, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } if (thd->copy_db_to(&db.str, &db.length)) - YYABORT; + MYSQL_YYABORT; $$= new sp_name(db, $1); if ($$) $$->init_qname(YYTHD); @@ -1549,7 +1945,8 @@ sp_name: create_function_tail: RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys { - LEX *lex=Lex; + THD *thd= YYTHD; + LEX *lex= thd->lex; if (lex->definer != NULL) { /* @@ -1559,7 +1956,13 @@ create_function_tail: and is considered a parsing error. */ my_error(ER_WRONG_USAGE, MYF(0), "SONAME", "DEFINER"); - YYABORT; + MYSQL_YYABORT; + } + if (is_native_function(thd, & lex->spname->m_name)) + { + my_error(ER_NATIVE_FCT_NAME_COLLISION, MYF(0), + lex->spname->m_name.str); + MYSQL_YYABORT; } lex->sql_command = SQLCOM_CREATE_FUNCTION; lex->udf.name = lex->spname->m_name; @@ -1578,13 +1981,13 @@ create_function_tail: if (lex->udf.type == UDFTYPE_AGGREGATE) { my_error(ER_SP_NO_AGGREGATE, MYF(0)); - YYABORT; + MYSQL_YYABORT; } if (lex->sphead) { my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "FUNCTION"); - YYABORT; + MYSQL_YYABORT; } /* Order is important here: new - reset - init */ sp= new sp_head(); @@ -1626,17 +2029,17 @@ create_function_tail: When collation support in SP is implemented, then this test should be removed. */ - if (($8 == FIELD_TYPE_STRING || $8 == MYSQL_TYPE_VARCHAR) + if (($8 == MYSQL_TYPE_STRING || $8 == MYSQL_TYPE_VARCHAR) && (lex->type & BINCMP_FLAG)) { my_error(ER_NOT_SUPPORTED_YET, MYF(0), "return value collation"); - YYABORT; + MYSQL_YYABORT; } if (sp->fill_field_definition(YYTHD, lex, (enum enum_field_types) $8, &sp->m_return_field_def)) - YYABORT; + MYSQL_YYABORT; bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); } @@ -1649,22 +2052,58 @@ create_function_tail: } sp_proc_stmt { - LEX *lex= Lex; + THD *thd= YYTHD; + LEX *lex= thd->lex; sp_head *sp= lex->sphead; if (sp->is_not_allowed_in_function("function")) - YYABORT; + MYSQL_YYABORT; lex->sql_command= SQLCOM_CREATE_SPFUNCTION; - sp->init_strings(YYTHD, lex); + sp->init_strings(thd, lex); if (!(sp->m_flags & sp_head::HAS_RETURN)) { my_error(ER_SP_NORETURN, MYF(0), sp->m_qname.str); - YYABORT; + MYSQL_YYABORT; + } + if (is_native_function(thd, & sp->m_name)) + { + /* + This warning will be printed when + [1] A client query is parsed, + [2] A stored function is loaded by db_load_routine. + Printing the warning for [2] is intentional, to cover the + following scenario: + - A user define a SF 'foo' using MySQL 5.N + - An application uses select foo(), and works. + - MySQL 5.{N+1} defines a new native function 'foo', as + part of a new feature. + - MySQL 5.{N+1} documentation is updated, and should mention + that there is a potential incompatible change in case of + existing stored function named 'foo'. + - The user deploys 5.{N+1}. At this point, 'select foo()' + means something different, and the user code is most likely + broken (it's only safe if the code is 'select db.foo()'). + With a warning printed when the SF is loaded (which has to occur + before the call), the warning will provide a hint explaining + the root cause of a later failure of 'select foo()'. + With no warning printed, the user code will fail with no + apparent reason. + Printing a warning each time db_load_routine is executed for + an ambiguous function is annoying, since that can happen a lot, + but in practice should not happen unless there *are* name + collisions. + If a collision exists, it should not be silenced but fixed. + */ + push_warning_printf(thd, + MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_NATIVE_FCT_NAME_COLLISION, + ER(ER_NATIVE_FCT_NAME_COLLISION), + sp->m_name.str); } /* Restore flag if it was cleared above */ - YYTHD->client_capabilities |= $<ulong_num>2; - sp->restore_thd_mem_root(YYTHD); + thd->client_capabilities |= $<ulong_num>2; + sp->restore_thd_mem_root(thd); } ; @@ -1724,15 +2163,20 @@ call: lex->value_list.empty(); sp_add_used_routine(lex, YYTHD, $2, TYPE_ENUM_PROCEDURE); } - '(' sp_cparam_list ')' {} + opt_sp_cparam_list {} ; /* CALL parameters */ -sp_cparam_list: +opt_sp_cparam_list: /* Empty */ - | sp_cparams + | '(' opt_sp_cparams ')' ; +opt_sp_cparams: + /* Empty */ + | sp_cparams + ; + sp_cparams: sp_cparams ',' expr { @@ -1784,7 +2228,7 @@ sp_fdparam: if (spc->find_variable(&$1, TRUE)) { my_error(ER_SP_DUP_PARAM, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } sp_variable_t *spvar= spc->push_variable(&$1, (enum enum_field_types)$3, @@ -1794,7 +2238,7 @@ sp_fdparam: (enum enum_field_types) $3, &spvar->field_def)) { - YYABORT; + MYSQL_YYABORT; } spvar->field_def.field_name= spvar->name.str; spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL; @@ -1821,7 +2265,7 @@ sp_pdparam: if (spc->find_variable(&$3, TRUE)) { my_error(ER_SP_DUP_PARAM, MYF(0), $3.str); - YYABORT; + MYSQL_YYABORT; } sp_variable_t *spvar= spc->push_variable(&$3, (enum enum_field_types)$4, @@ -1831,7 +2275,7 @@ sp_pdparam: (enum enum_field_types) $4, &spvar->field_def)) { - YYABORT; + MYSQL_YYABORT; } spvar->field_def.field_name= spvar->name.str; spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL; @@ -1870,13 +2314,13 @@ sp_decls: { /* Variable or condition following cursor or handler */ my_message(ER_SP_VARCOND_AFTER_CURSHNDLR, ER(ER_SP_VARCOND_AFTER_CURSHNDLR), MYF(0)); - YYABORT; + MYSQL_YYABORT; } if ($2.curs && $1.hndlrs) { /* Cursor following handler */ my_message(ER_SP_CURSOR_AFTER_HANDLER, ER(ER_SP_CURSOR_AFTER_HANDLER), MYF(0)); - YYABORT; + MYSQL_YYABORT; } $$.vars= $1.vars + $2.vars; $$.conds= $1.conds + $2.conds; @@ -1901,7 +2345,6 @@ sp_decl: uint num_vars= pctx->context_var_count(); enum enum_field_types var_type= (enum enum_field_types) $4; Item *dflt_value_item= $5; - create_field *create_field_op; if (!dflt_value_item) { @@ -1915,7 +2358,7 @@ sp_decl: sp_variable_t *spvar= pctx->find_variable(var_idx); if (!spvar) - YYABORT; + MYSQL_YYABORT; spvar->type= var_type; spvar->dflt= dflt_value_item; @@ -1923,7 +2366,7 @@ sp_decl: if (lex->sphead->fill_field_definition(YYTHD, lex, var_type, &spvar->field_def)) { - YYABORT; + MYSQL_YYABORT; } spvar->field_def.field_name= spvar->name.str; @@ -1951,7 +2394,7 @@ sp_decl: if (spc->find_cond(&$2, TRUE)) { my_error(ER_SP_DUP_COND, MYF(0), $2.str); - YYABORT; + MYSQL_YYABORT; } YYTHD->lex->spcont->push_cond(&$2, $5); $$.vars= $$.hndlrs= $$.curs= 0; @@ -2009,7 +2452,7 @@ sp_decl: { my_error(ER_SP_DUP_CURS, MYF(0), $2.str); delete $5; - YYABORT; + MYSQL_YYABORT; } i= new sp_instr_cpush(sp->instructions(), ctx, $5, ctx->current_cursor_count()); @@ -2040,13 +2483,13 @@ sp_cursor_stmt: { my_message(ER_SP_BAD_CURSOR_QUERY, ER(ER_SP_BAD_CURSOR_QUERY), MYF(0)); - YYABORT; + MYSQL_YYABORT; } if (lex->result) { my_message(ER_SP_BAD_CURSOR_SELECT, ER(ER_SP_BAD_CURSOR_SELECT), MYF(0)); - YYABORT; + MYSQL_YYABORT; } lex->sp_lex_in_use= TRUE; $$= lex; @@ -2070,7 +2513,7 @@ sp_hcond_list: if (ctx->find_handler($1)) { my_message(ER_SP_DUP_HANDLER, ER(ER_SP_DUP_HANDLER), MYF(0)); - YYABORT; + MYSQL_YYABORT; } else { @@ -2091,7 +2534,7 @@ sp_hcond_list: if (ctx->find_handler($3)) { my_message(ER_SP_DUP_HANDLER, ER(ER_SP_DUP_HANDLER), MYF(0)); - YYABORT; + MYSQL_YYABORT; } else { @@ -2117,7 +2560,7 @@ sp_cond: if (!sp_cond_check(&$3)) { my_error(ER_SP_BAD_SQLSTATE, MYF(0), $3.str); - YYABORT; + MYSQL_YYABORT; } $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); $$->type= sp_cond_type_t::state; @@ -2142,7 +2585,7 @@ sp_hcond: if ($$ == NULL) { my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } } | SQLWARNING_SYM /* SQLSTATEs 01??? */ @@ -2173,7 +2616,7 @@ sp_decl_idents: if (spc->find_variable(&$1, TRUE)) { my_error(ER_SP_DUP_VAR, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } spc->push_variable(&$1, (enum_field_types)0, sp_param_in); $$= 1; @@ -2188,7 +2631,7 @@ sp_decl_idents: if (spc->find_variable(&$3, TRUE)) { my_error(ER_SP_DUP_VAR, MYF(0), $3.str); - YYABORT; + MYSQL_YYABORT; } spc->push_variable(&$3, (enum_field_types)0, sp_param_in); $$= $1 + 1; @@ -2204,8 +2647,7 @@ sp_proc_stmt: sp_proc_stmt_statement | sp_proc_stmt_return | sp_proc_stmt_if - | sp_proc_stmt_case_simple - | sp_proc_stmt_case + | case_stmt_specification | sp_labeled_control | sp_proc_stmt_unlabeled | sp_proc_stmt_leave @@ -2237,7 +2679,7 @@ sp_proc_stmt_statement: if (lex->sql_command == SQLCOM_CHANGE_DB) { /* "USE db" doesn't work in a procedure */ my_error(ER_SP_BADSTATEMENT, MYF(0), "USE"); - YYABORT; + MYSQL_YYABORT; } /* Don't add an instruction for SET statements, since all @@ -2280,7 +2722,7 @@ sp_proc_stmt_return: if (sp->m_type != TYPE_ENUM_FUNCTION) { my_message(ER_SP_BADRETURN, ER(ER_SP_BADRETURN), MYF(0)); - YYABORT; + MYSQL_YYABORT; } else { @@ -2295,49 +2737,6 @@ sp_proc_stmt_return: } ; -sp_proc_stmt_case_simple: - CASE_SYM WHEN_SYM - { - Lex->sphead->m_flags&= ~sp_head::IN_SIMPLE_CASE; - Lex->sphead->new_cont_backpatch(NULL); - } - sp_case END CASE_SYM { Lex->sphead->do_cont_backpatch(); } - ; - -sp_proc_stmt_case: - CASE_SYM - { - Lex->sphead->reset_lex(YYTHD); - Lex->sphead->new_cont_backpatch(NULL); - } - expr WHEN_SYM - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - sp_pcontext *parsing_ctx= lex->spcont; - int case_expr_id= parsing_ctx->register_case_expr(); - sp_instr_set_case_expr *i; - - if (parsing_ctx->push_case_expr_id(case_expr_id)) - YYABORT; - - i= new sp_instr_set_case_expr(sp->instructions(), - parsing_ctx, - case_expr_id, - $3, - lex); - sp->add_cont_backpatch(i); - sp->add_instr(i); - sp->m_flags|= sp_head::IN_SIMPLE_CASE; - sp->restore_lex(YYTHD); - } - sp_case END CASE_SYM - { - Lex->spcont->pop_case_expr_id(); - Lex->sphead->do_cont_backpatch(); - } - ; - sp_proc_stmt_unlabeled: { /* Unlabeled controls get a secret label. */ LEX *lex= Lex; @@ -2363,7 +2762,7 @@ sp_proc_stmt_leave: if (! lab) { my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", $2.str); - YYABORT; + MYSQL_YYABORT; } else { @@ -2395,7 +2794,7 @@ sp_proc_stmt_iterate: if (! lab || lab->type != SP_LAB_ITER) { my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", $2.str); - YYABORT; + MYSQL_YYABORT; } else { @@ -2426,7 +2825,7 @@ sp_proc_stmt_open: if (! lex->spcont->find_cursor(&$2, &offset)) { my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str); - YYABORT; + MYSQL_YYABORT; } i= new sp_instr_copen(sp->instructions(), lex->spcont, offset); sp->add_instr(i); @@ -2444,7 +2843,7 @@ sp_proc_stmt_fetch: if (! lex->spcont->find_cursor(&$3, &offset)) { my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $3.str); - YYABORT; + MYSQL_YYABORT; } i= new sp_instr_cfetch(sp->instructions(), lex->spcont, offset); sp->add_instr(i); @@ -2464,7 +2863,7 @@ sp_proc_stmt_close: if (! lex->spcont->find_cursor(&$2, &offset)) { my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str); - YYABORT; + MYSQL_YYABORT; } i= new sp_instr_cclose(sp->instructions(), lex->spcont, offset); sp->add_instr(i); @@ -2488,7 +2887,7 @@ sp_fetch_list: if (!spc || !(spv = spc->find_variable(&$1))) { my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } else { @@ -2509,7 +2908,7 @@ sp_fetch_list: if (!spc || !(spv = spc->find_variable(&$3))) { my_error(ER_SP_UNDECLARED_VAR, MYF(0), $3.str); - YYABORT; + MYSQL_YYABORT; } else { @@ -2562,72 +2961,114 @@ sp_elseifs: | ELSE sp_proc_stmts1 ; -sp_case: - { Lex->sphead->reset_lex(YYTHD); } - expr THEN_SYM - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - sp_pcontext *ctx= Lex->spcont; - uint ip= sp->instructions(); - sp_instr_jump_if_not *i; - - if (! (sp->m_flags & sp_head::IN_SIMPLE_CASE)) - i= new sp_instr_jump_if_not(ip, ctx, $2, lex); - else - { /* Simple case: <caseval> = <whenval> */ +case_stmt_specification: + simple_case_stmt + | searched_case_stmt + ; - Item_case_expr *var; - Item *expr; +simple_case_stmt: + CASE_SYM + { + LEX *lex= Lex; + case_stmt_action_case(lex); + lex->sphead->reset_lex(YYTHD); /* For expr $3 */ + } + expr + { + LEX *lex= Lex; + if (case_stmt_action_expr(lex, $3)) + MYSQL_YYABORT; - var= new Item_case_expr(ctx->get_current_case_expr_id()); + lex->sphead->restore_lex(YYTHD); /* For expr $3 */ + } + simple_when_clause_list + else_clause_opt + END + CASE_SYM + { + LEX *lex= Lex; + case_stmt_action_end_case(lex, true); + } + ; -#ifndef DBUG_OFF - if (var) - var->m_sp= sp; -#endif +searched_case_stmt: + CASE_SYM + { + LEX *lex= Lex; + case_stmt_action_case(lex); + } + searched_when_clause_list + else_clause_opt + END + CASE_SYM + { + LEX *lex= Lex; + case_stmt_action_end_case(lex, false); + } + ; - expr= new Item_func_eq(var, $2); +simple_when_clause_list: + simple_when_clause + | simple_when_clause_list simple_when_clause + ; - i= new sp_instr_jump_if_not(ip, ctx, expr, lex); - } - sp->push_backpatch(i, ctx->push_label((char *)"", 0)); - sp->add_cont_backpatch(i); - sp->add_instr(i); - sp->restore_lex(YYTHD); - } - sp_proc_stmts1 - { - sp_head *sp= Lex->sphead; - sp_pcontext *ctx= Lex->spcont; - uint ip= sp->instructions(); - sp_instr_jump *i = new sp_instr_jump(ip, ctx); +searched_when_clause_list: + searched_when_clause + | searched_when_clause_list searched_when_clause + ; - sp->add_instr(i); - sp->backpatch(ctx->pop_label()); - sp->push_backpatch(i, ctx->push_label((char *)"", 0)); - } - sp_whens - { - LEX *lex= Lex; +simple_when_clause: + WHEN_SYM + { + Lex->sphead->reset_lex(YYTHD); /* For expr $3 */ + } + expr + { + /* Simple case: <caseval> = <whenval> */ - lex->sphead->backpatch(lex->spcont->pop_label()); - } - ; + LEX *lex= Lex; + case_stmt_action_when(lex, $3, true); + lex->sphead->restore_lex(YYTHD); /* For expr $3 */ + } + THEN_SYM + sp_proc_stmts1 + { + LEX *lex= Lex; + case_stmt_action_then(lex); + } + ; -sp_whens: - /* Empty */ - { - sp_head *sp= Lex->sphead; - uint ip= sp->instructions(); - sp_instr_error *i= new sp_instr_error(ip, Lex->spcont, - ER_SP_CASE_NOT_FOUND); +searched_when_clause: + WHEN_SYM + { + Lex->sphead->reset_lex(YYTHD); /* For expr $3 */ + } + expr + { + LEX *lex= Lex; + case_stmt_action_when(lex, $3, false); + lex->sphead->restore_lex(YYTHD); /* For expr $3 */ + } + THEN_SYM + sp_proc_stmts1 + { + LEX *lex= Lex; + case_stmt_action_then(lex); + } + ; - sp->add_instr(i); - } - | ELSE sp_proc_stmts1 {} - | WHEN_SYM sp_case {} - ; +else_clause_opt: + /* empty */ + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + uint ip= sp->instructions(); + sp_instr_error *i= new sp_instr_error(ip, lex->spcont, + ER_SP_CASE_NOT_FOUND); + sp->add_instr(i); + } + | ELSE sp_proc_stmts1 + ; sp_labeled_control: label_ident ':' @@ -2639,7 +3080,7 @@ sp_labeled_control: if (lab) { my_error(ER_SP_LABEL_REDEFINE, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } else { @@ -2660,7 +3101,7 @@ sp_labeled_control: my_strcasecmp(system_charset_info, $5.str, lab->name) != 0) { my_error(ER_SP_LABEL_MISMATCH, MYF(0), $5.str); - YYABORT; + MYSQL_YYABORT; } } lex->sphead->backpatch(lex->spcont->pop_label()); @@ -2775,11 +3216,11 @@ trg_event: ALTER TABLESPACE name CHANGE DATAFILE ... ALTER TABLESPACE name ADD DATAFILE ... ALTER TABLESPACE name access_mode - CREATE LOGFILE GROUP name ... - ALTER LOGFILE GROUP name ADD UNDOFILE .. - ALTER LOGFILE GROUP name ADD REDOFILE .. + CREATE LOGFILE GROUP_SYM name ... + ALTER LOGFILE GROUP_SYM name ADD UNDOFILE .. + ALTER LOGFILE GROUP_SYM name ADD REDOFILE .. DROP TABLESPACE name - DROP LOGFILE GROUP name + DROP LOGFILE GROUP_SYM name */ change_tablespace_access: tablespace_name @@ -2801,7 +3242,7 @@ tablespace_info: opt_logfile_group_name: /* empty */ {} - | USE_SYM LOGFILE_SYM GROUP ident + | USE_SYM LOGFILE_SYM GROUP_SYM ident { LEX *lex= Lex; lex->alter_tablespace_info->logfile_group_name= $4.str; @@ -3041,7 +3482,7 @@ opt_ts_nodegroup: if (lex->alter_tablespace_info->nodegroup_id != UNDEF_NODEGROUP) { my_error(ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"NODEGROUP"); - YYABORT; + MYSQL_YYABORT; } lex->alter_tablespace_info->nodegroup_id= $3; }; @@ -3053,7 +3494,7 @@ opt_ts_comment: if (lex->alter_tablespace_info->ts_comment != NULL) { my_error(ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"COMMENT"); - YYABORT; + MYSQL_YYABORT; } lex->alter_tablespace_info->ts_comment= $3.str; }; @@ -3066,7 +3507,7 @@ opt_ts_engine: { my_error(ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0), "STORAGE ENGINE"); - YYABORT; + MYSQL_YYABORT; } lex->alter_tablespace_info->storage_engine= $4; }; @@ -3088,7 +3529,7 @@ ts_wait: if (!(lex->alter_tablespace_info->wait_until_completed)) { my_error(ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"NO_WAIT"); - YYABORT; + MYSQL_YYABORT; } lex->alter_tablespace_info->wait_until_completed= FALSE; }; @@ -3097,11 +3538,11 @@ size_number: real_ulong_num { $$= $1;} | IDENT { - ulonglong number, test_number; + ulonglong number; uint text_shift_number= 0; longlong prefix_number; char *start_ptr= $1.str; - uint str_len= strlen(start_ptr); + uint str_len= $1.length; char *end_ptr= start_ptr + str_len; int error; prefix_number= my_strtoll10(start_ptr, &end_ptr, &error); @@ -3122,20 +3563,20 @@ size_number: default: { my_error(ER_WRONG_SIZE_NUMBER, MYF(0)); - YYABORT; + MYSQL_YYABORT; } } if (prefix_number >> 31) { my_error(ER_SIZE_OVERFLOW_ERROR, MYF(0)); - YYABORT; + MYSQL_YYABORT; } number= prefix_number << text_shift_number; } else { my_error(ER_WRONG_SIZE_NUMBER, MYF(0)); - YYABORT; + MYSQL_YYABORT; } $$= number; } @@ -3152,26 +3593,26 @@ create2: create3 {} | LIKE table_ident { - LEX *lex=Lex; - THD *thd= lex->thd; + THD *thd= YYTHD; + LEX *lex= thd->lex; if (!(lex->like_name= $2)) - YYABORT; + MYSQL_YYABORT; if ($2->db.str == NULL && thd->copy_db_to(&($2->db.str), &($2->db.length))) { - YYABORT; + MYSQL_YYABORT; } } | '(' LIKE table_ident ')' { - LEX *lex=Lex; - THD *thd= lex->thd; + THD *thd= YYTHD; + LEX *lex= thd->lex; if (!(lex->like_name= $3)) - YYABORT; + MYSQL_YYABORT; if ($3->db.str == NULL && thd->copy_db_to(&($3->db.str), &($3->db.length))) { - YYABORT; + MYSQL_YYABORT; } } ; @@ -3225,17 +3666,24 @@ opt_partitioning: partitioning: PARTITION_SYM { +#ifdef WITH_PARTITION_STORAGE_ENGINE LEX *lex= Lex; lex->part_info= new partition_info(); if (!lex->part_info) { mem_alloc_error(sizeof(partition_info)); - YYABORT; + MYSQL_YYABORT; } if (lex->sql_command == SQLCOM_ALTER_TABLE) { lex->alter_info.flags|= ALTER_PARTITION; } +#else + my_error(ER_FEATURE_DISABLED, MYF(0), + "partitioning", "--with-partition"); + MYSQL_YYABORT; +#endif + } partition ; @@ -3246,8 +3694,8 @@ partition_entry: LEX *lex= Lex; if (!lex->part_info) { - yyerror(ER(ER_PARTITION_ENTRY_ERROR)); - YYABORT; + my_parse_error(ER(ER_PARTITION_ENTRY_ERROR)); + MYSQL_YYABORT; } /* We enter here when opening the frm file to translate @@ -3301,7 +3749,7 @@ part_field_item: if (Lex->part_info->part_field_list.push_back($1.str)) { mem_alloc_error(1); - YYABORT; + MYSQL_YYABORT; } } ; @@ -3340,7 +3788,7 @@ opt_no_parts: if (no_parts == 0) { my_error(ER_NO_PARTS_ERROR, MYF(0), "partitions"); - YYABORT; + MYSQL_YYABORT; } lex->part_info->no_parts= no_parts; @@ -3374,7 +3822,7 @@ sub_part_field_item: if (Lex->part_info->subpart_field_list.push_back($1.str)) { mem_alloc_error(1); - YYABORT; + MYSQL_YYABORT; } } ; @@ -3388,8 +3836,8 @@ part_func_expr: lex->safe_to_cache_query= 1; if (not_corr_func) { - yyerror(ER(ER_CONST_EXPR_IN_PARTITION_FUNC_ERROR)); - YYABORT; + my_parse_error(ER(ER_CONST_EXPR_IN_PARTITION_FUNC_ERROR)); + MYSQL_YYABORT; } $$=$1; } @@ -3404,7 +3852,7 @@ opt_no_subparts: if (no_parts == 0) { my_error(ER_NO_PARTS_ERROR, MYF(0), "subpartitions"); - YYABORT; + MYSQL_YYABORT; } lex->part_info->no_subparts= no_parts; lex->part_info->use_default_no_subpartitions= FALSE; @@ -3424,8 +3872,8 @@ part_defs: if (part_info->no_parts != count_curr_parts) { - yyerror(ER(ER_PARTITION_WRONG_NO_PART_ERROR)); - YYABORT; + my_parse_error(ER(ER_PARTITION_WRONG_NO_PART_ERROR)); + MYSQL_YYABORT; } } else if (count_curr_parts > 0) @@ -3447,12 +3895,11 @@ part_definition: LEX *lex= Lex; partition_info *part_info= lex->part_info; partition_element *p_elem= new partition_element(); - uint part_id= part_info->partitions.elements; if (!p_elem || part_info->partitions.push_back(p_elem)) { mem_alloc_error(sizeof(partition_element)); - YYABORT; + MYSQL_YYABORT; } p_elem->part_state= PART_NORMAL; part_info->curr_part_elem= p_elem; @@ -3486,13 +3933,13 @@ opt_part_values: { my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), "RANGE", "LESS THAN"); - YYABORT; + MYSQL_YYABORT; } if (lex->part_info->part_type == LIST_PARTITION) { my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), "LIST", "IN"); - YYABORT; + MYSQL_YYABORT; } } else @@ -3507,7 +3954,7 @@ opt_part_values: { my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), "RANGE", "LESS THAN"); - YYABORT; + MYSQL_YYABORT; } } else @@ -3522,7 +3969,7 @@ opt_part_values: { my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), "LIST", "IN"); - YYABORT; + MYSQL_YYABORT; } } else @@ -3536,8 +3983,8 @@ part_func_max: LEX *lex= Lex; if (lex->part_info->defined_max_value) { - yyerror(ER(ER_PARTITION_MAXVALUE_ERROR)); - YYABORT; + my_parse_error(ER(ER_PARTITION_MAXVALUE_ERROR)); + MYSQL_YYABORT; } lex->part_info->defined_max_value= TRUE; lex->part_info->curr_part_elem->max_value= TRUE; @@ -3547,13 +3994,13 @@ part_func_max: { if (Lex->part_info->defined_max_value) { - yyerror(ER(ER_PARTITION_MAXVALUE_ERROR)); - YYABORT; + my_parse_error(ER(ER_PARTITION_MAXVALUE_ERROR)); + MYSQL_YYABORT; } if (Lex->part_info->curr_part_elem->has_null_value) { - yyerror(ER(ER_NULL_IN_VALUES_LESS_THAN)); - YYABORT; + my_parse_error(ER(ER_NULL_IN_VALUES_LESS_THAN)); + MYSQL_YYABORT; } } ; @@ -3590,7 +4037,7 @@ part_list_item: list_val_list.push_back(value_ptr)) { mem_alloc_error(sizeof(part_elem_value)); - YYABORT; + MYSQL_YYABORT; } } ; @@ -3599,11 +4046,8 @@ part_bit_expr: bit_expr { Item *part_expr= $1; - bool not_corr_func; - int part_expression_ok= 1; - LEX *lex= Lex; THD *thd= YYTHD; - longlong item_value; + LEX *lex= thd->lex; Name_resolution_context *context= &lex->current_select->context; TABLE_LIST *save_list= context->table_list; const char *save_where= thd->where; @@ -3616,13 +4060,13 @@ part_bit_expr: if (!value_ptr) { mem_alloc_error(sizeof(part_elem_value)); - YYABORT; + MYSQL_YYABORT; } if (part_expr->walk(&Item::check_partition_func_processor, 0, NULL)) { my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); - YYABORT; + MYSQL_YYABORT; } if (part_expr->fix_fields(YYTHD, (Item**)0) || ((context->table_list= save_list), FALSE) || @@ -3630,7 +4074,7 @@ part_bit_expr: (!lex->safe_to_cache_query)) { my_error(ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR, MYF(0)); - YYABORT; + MYSQL_YYABORT; } thd->where= save_where; value_ptr->value= part_expr->val_int(); @@ -3643,15 +4087,15 @@ part_bit_expr: if (Lex->part_info->curr_part_elem->has_null_value) { my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0)); - YYABORT; + MYSQL_YYABORT; } Lex->part_info->curr_part_elem->has_null_value= TRUE; } else if (part_expr->result_type() != INT_RESULT && !part_expr->null_value) { - yyerror(ER(ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR)); - YYABORT; + my_parse_error(ER(ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR)); + MYSQL_YYABORT; } $$= value_ptr; } @@ -3663,8 +4107,8 @@ opt_sub_partition: if (Lex->part_info->no_subparts != 0 && !Lex->part_info->use_default_subpartitions) { - yyerror(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); - YYABORT; + my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); + MYSQL_YYABORT; } } | '(' sub_part_list ')' @@ -3676,16 +4120,16 @@ opt_sub_partition: if (part_info->no_subparts != part_info->count_curr_subparts) { - yyerror(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); - YYABORT; + my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); + MYSQL_YYABORT; } } else if (part_info->count_curr_subparts > 0) { if (part_info->partitions.elements > 1) { - yyerror(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); - YYABORT; + my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); + MYSQL_YYABORT; } part_info->no_subparts= part_info->count_curr_subparts; } @@ -3709,7 +4153,7 @@ sub_part_definition: curr_part->subpartitions.push_back(sub_p_elem)) { mem_alloc_error(sizeof(partition_element)); - YYABORT; + MYSQL_YYABORT; } part_info->curr_part_elem= sub_p_elem; part_info->use_default_subpartitions= FALSE; @@ -3744,11 +4188,11 @@ opt_part_option: lex->part_info->default_engine_type= $4; } | NODEGROUP_SYM opt_equal real_ulong_num - { Lex->part_info->curr_part_elem->nodegroup_id= $3; } + { Lex->part_info->curr_part_elem->nodegroup_id= (uint16) $3; } | MAX_ROWS opt_equal real_ulonglong_num - { Lex->part_info->curr_part_elem->part_max_rows= $3; } + { Lex->part_info->curr_part_elem->part_max_rows= (ha_rows) $3; } | MIN_ROWS opt_equal real_ulonglong_num - { Lex->part_info->curr_part_elem->part_min_rows= $3; } + { Lex->part_info->curr_part_elem->part_min_rows= (ha_rows) $3; } | DATA_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys { Lex->part_info->curr_part_elem->data_file_name= $4.str; } | INDEX_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys @@ -3861,8 +4305,8 @@ create_table_option: Lex->create_info.table_options|= HA_OPTION_PACK_KEYS; break; default: - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS; } @@ -3896,8 +4340,8 @@ create_table_option: | DATA_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys { Lex->create_info.data_file_name= $4.str; Lex->create_info.used_fields|= HA_CREATE_USED_DATADIR; } | INDEX_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys { Lex->create_info.index_file_name= $4.str; Lex->create_info.used_fields|= HA_CREATE_USED_INDEXDIR; } | TABLESPACE ident {Lex->create_info.tablespace= $2.str;} - | STORAGE_SYM DISK_SYM {Lex->create_info.store_on_disk= TRUE;} - | STORAGE_SYM MEMORY_SYM {Lex->create_info.store_on_disk= FALSE;} + | STORAGE_SYM DISK_SYM {Lex->create_info.storage_media= HA_SM_DISK;} + | STORAGE_SYM MEMORY_SYM {Lex->create_info.storage_media= HA_SM_MEMORY;} | CONNECTION_SYM opt_equal TEXT_STRING_sys { Lex->create_info.connect_string.str= $3.str; Lex->create_info.connect_string.length= $3.length; Lex->create_info.used_fields|= HA_CREATE_USED_CONNECTION; } | KEY_BLOCK_SIZE opt_equal ulong_num { @@ -3917,7 +4361,7 @@ default_charset: my_error(ER_CONFLICTING_DECLARATIONS, MYF(0), "CHARACTER SET ", cinfo->default_table_charset->csname, "CHARACTER SET ", $4->csname); - YYABORT; + MYSQL_YYABORT; } Lex->create_info.default_table_charset= $4; Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; @@ -3933,25 +4377,37 @@ default_collation: { my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), $4->name, cinfo->default_table_charset->csname); - YYABORT; + MYSQL_YYABORT; } Lex->create_info.default_table_charset= $4; Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; }; +known_storage_engines: + ident_or_text + { + $$ = ha_resolve_by_name(YYTHD, &$1); + if ($$ == NULL) + { + my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str); + MYSQL_YYABORT; + } + } + ; + storage_engines: ident_or_text { $$ = ha_resolve_by_name(YYTHD, &$1); if ($$ == NULL) - if (YYTHD->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION) - { - my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str); - YYABORT; - } - else { - push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_ERROR, + if (YYTHD->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION) + { + my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str); + MYSQL_YYABORT; + } + + push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_STORAGE_ENGINE, ER(ER_UNKNOWN_STORAGE_ENGINE), $1.str); } @@ -4008,8 +4464,8 @@ key_def: LEX *lex=Lex; if ($1 != Key::FULLTEXT && lex->key_create_info.parser_name.str) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } lex->key_list.push_back(new Key($1,$2, &lex->key_create_info, 0, lex->col_list)); @@ -4088,37 +4544,37 @@ field_spec: &lex->comment, lex->change,&lex->interval_list,lex->charset, lex->uint_geom_type)) - YYABORT; + MYSQL_YYABORT; }; type: int_type opt_len field_options { $$=$1; } | real_type opt_precision field_options { $$=$1; } - | FLOAT_SYM float_options field_options { $$=FIELD_TYPE_FLOAT; } + | FLOAT_SYM float_options field_options { $$=MYSQL_TYPE_FLOAT; } | BIT_SYM { Lex->length= (char*) "1"; - $$=FIELD_TYPE_BIT; } + $$=MYSQL_TYPE_BIT; } | BIT_SYM '(' NUM ')' { Lex->length= $3.str; - $$=FIELD_TYPE_BIT; } + $$=MYSQL_TYPE_BIT; } | BOOL_SYM { Lex->length=(char*) "1"; - $$=FIELD_TYPE_TINY; } + $$=MYSQL_TYPE_TINY; } | BOOLEAN_SYM { Lex->length=(char*) "1"; - $$=FIELD_TYPE_TINY; } + $$=MYSQL_TYPE_TINY; } | char '(' NUM ')' opt_binary { Lex->length=$3.str; - $$=FIELD_TYPE_STRING; } + $$=MYSQL_TYPE_STRING; } | char opt_binary { Lex->length=(char*) "1"; - $$=FIELD_TYPE_STRING; } + $$=MYSQL_TYPE_STRING; } | nchar '(' NUM ')' opt_bin_mod { Lex->length=$3.str; - $$=FIELD_TYPE_STRING; + $$=MYSQL_TYPE_STRING; Lex->charset=national_charset_info; } | nchar opt_bin_mod { Lex->length=(char*) "1"; - $$=FIELD_TYPE_STRING; + $$=MYSQL_TYPE_STRING; Lex->charset=national_charset_info; } | BINARY '(' NUM ')' { Lex->length=$3.str; Lex->charset=&my_charset_bin; - $$=FIELD_TYPE_STRING; } + $$=MYSQL_TYPE_STRING; } | BINARY { Lex->length= (char*) "1"; Lex->charset=&my_charset_bin; - $$=FIELD_TYPE_STRING; } + $$=MYSQL_TYPE_STRING; } | varchar '(' NUM ')' opt_binary { Lex->length=$3.str; $$= MYSQL_TYPE_VARCHAR; } | nvarchar '(' NUM ')' opt_bin_mod { Lex->length=$3.str; @@ -4127,64 +4583,64 @@ type: | VARBINARY '(' NUM ')' { Lex->length=$3.str; Lex->charset=&my_charset_bin; $$= MYSQL_TYPE_VARCHAR; } - | YEAR_SYM opt_len field_options { $$=FIELD_TYPE_YEAR; } - | DATE_SYM { $$=FIELD_TYPE_DATE; } - | TIME_SYM { $$=FIELD_TYPE_TIME; } + | YEAR_SYM opt_len field_options { $$=MYSQL_TYPE_YEAR; } + | DATE_SYM { $$=MYSQL_TYPE_DATE; } + | TIME_SYM { $$=MYSQL_TYPE_TIME; } | TIMESTAMP opt_len { if (YYTHD->variables.sql_mode & MODE_MAXDB) - $$=FIELD_TYPE_DATETIME; + $$=MYSQL_TYPE_DATETIME; else { /* Unlike other types TIMESTAMP fields are NOT NULL by default. */ Lex->type|= NOT_NULL_FLAG; - $$=FIELD_TYPE_TIMESTAMP; + $$=MYSQL_TYPE_TIMESTAMP; } } - | DATETIME { $$=FIELD_TYPE_DATETIME; } + | DATETIME { $$=MYSQL_TYPE_DATETIME; } | TINYBLOB { Lex->charset=&my_charset_bin; - $$=FIELD_TYPE_TINY_BLOB; } + $$=MYSQL_TYPE_TINY_BLOB; } | BLOB_SYM opt_len { Lex->charset=&my_charset_bin; - $$=FIELD_TYPE_BLOB; } + $$=MYSQL_TYPE_BLOB; } | spatial_type { #ifdef HAVE_SPATIAL Lex->charset=&my_charset_bin; Lex->uint_geom_type= (uint)$1; - $$=FIELD_TYPE_GEOMETRY; + $$=MYSQL_TYPE_GEOMETRY; #else my_error(ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name, sym_group_geom.needed_define); - YYABORT; + MYSQL_YYABORT; #endif } | MEDIUMBLOB { Lex->charset=&my_charset_bin; - $$=FIELD_TYPE_MEDIUM_BLOB; } + $$=MYSQL_TYPE_MEDIUM_BLOB; } | LONGBLOB { Lex->charset=&my_charset_bin; - $$=FIELD_TYPE_LONG_BLOB; } + $$=MYSQL_TYPE_LONG_BLOB; } | LONG_SYM VARBINARY { Lex->charset=&my_charset_bin; - $$=FIELD_TYPE_MEDIUM_BLOB; } - | LONG_SYM varchar opt_binary { $$=FIELD_TYPE_MEDIUM_BLOB; } - | TINYTEXT opt_binary { $$=FIELD_TYPE_TINY_BLOB; } - | TEXT_SYM opt_len opt_binary { $$=FIELD_TYPE_BLOB; } - | MEDIUMTEXT opt_binary { $$=FIELD_TYPE_MEDIUM_BLOB; } - | LONGTEXT opt_binary { $$=FIELD_TYPE_LONG_BLOB; } + $$=MYSQL_TYPE_MEDIUM_BLOB; } + | LONG_SYM varchar opt_binary { $$=MYSQL_TYPE_MEDIUM_BLOB; } + | TINYTEXT opt_binary { $$=MYSQL_TYPE_TINY_BLOB; } + | TEXT_SYM opt_len opt_binary { $$=MYSQL_TYPE_BLOB; } + | MEDIUMTEXT opt_binary { $$=MYSQL_TYPE_MEDIUM_BLOB; } + | LONGTEXT opt_binary { $$=MYSQL_TYPE_LONG_BLOB; } | DECIMAL_SYM float_options field_options - { $$=FIELD_TYPE_NEWDECIMAL;} + { $$=MYSQL_TYPE_NEWDECIMAL;} | NUMERIC_SYM float_options field_options - { $$=FIELD_TYPE_NEWDECIMAL;} + { $$=MYSQL_TYPE_NEWDECIMAL;} | FIXED_SYM float_options field_options - { $$=FIELD_TYPE_NEWDECIMAL;} + { $$=MYSQL_TYPE_NEWDECIMAL;} | ENUM {Lex->interval_list.empty();} '(' string_list ')' opt_binary - { $$=FIELD_TYPE_ENUM; } + { $$=MYSQL_TYPE_ENUM; } | SET { Lex->interval_list.empty();} '(' string_list ')' opt_binary - { $$=FIELD_TYPE_SET; } - | LONG_SYM opt_binary { $$=FIELD_TYPE_MEDIUM_BLOB; } + { $$=MYSQL_TYPE_SET; } + | LONG_SYM opt_binary { $$=MYSQL_TYPE_MEDIUM_BLOB; } | SERIAL_SYM { - $$=FIELD_TYPE_LONGLONG; + $$=MYSQL_TYPE_LONGLONG; Lex->type|= (AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNSIGNED_FLAG | UNIQUE_FLAG); } @@ -4226,17 +4682,17 @@ nvarchar: ; int_type: - INT_SYM { $$=FIELD_TYPE_LONG; } - | TINYINT { $$=FIELD_TYPE_TINY; } - | SMALLINT { $$=FIELD_TYPE_SHORT; } - | MEDIUMINT { $$=FIELD_TYPE_INT24; } - | BIGINT { $$=FIELD_TYPE_LONGLONG; }; + INT_SYM { $$=MYSQL_TYPE_LONG; } + | TINYINT { $$=MYSQL_TYPE_TINY; } + | SMALLINT { $$=MYSQL_TYPE_SHORT; } + | MEDIUMINT { $$=MYSQL_TYPE_INT24; } + | BIGINT { $$=MYSQL_TYPE_LONGLONG; }; real_type: REAL { $$= YYTHD->variables.sql_mode & MODE_REAL_AS_FLOAT ? - FIELD_TYPE_FLOAT : FIELD_TYPE_DOUBLE; } - | DOUBLE_SYM { $$=FIELD_TYPE_DOUBLE; } - | DOUBLE_SYM PRECISION { $$=FIELD_TYPE_DOUBLE; }; + MYSQL_TYPE_FLOAT : MYSQL_TYPE_DOUBLE; } + | DOUBLE_SYM { $$=MYSQL_TYPE_DOUBLE; } + | DOUBLE_SYM PRECISION { $$=MYSQL_TYPE_DOUBLE; }; float_options: @@ -4318,7 +4774,7 @@ attribute: { my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), $2->name,Lex->charset->csname); - YYABORT; + MYSQL_YYABORT; } else { @@ -4343,7 +4799,7 @@ charset_name: if (!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0)))) { my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } } | BINARY { $$= &my_charset_bin; } @@ -4353,6 +4809,10 @@ charset_name_or_default: charset_name { $$=$1; } | DEFAULT { $$=NULL; } ; +opt_load_data_charset: + /* Empty */ { $$= NULL; } + | charset charset_name_or_default { $$= $2; } + ; old_or_new_charset_name: ident_or_text @@ -4361,7 +4821,7 @@ old_or_new_charset_name: !($$=get_old_charset_by_name($1.str))) { my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } } | BINARY { $$= &my_charset_bin; } @@ -4377,7 +4837,7 @@ collation_name: if (!($$=get_charset_by_name($1.str,MYF(0)))) { my_error(ER_UNKNOWN_COLLATION, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } }; @@ -4404,7 +4864,7 @@ opt_binary: MY_CS_PRIMARY,MYF(0)))) { my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), "ucs2"); - YYABORT; + MYSQL_YYABORT; } } | charset charset_name opt_bin_mod { Lex->charset=$2; } @@ -4423,7 +4883,7 @@ opt_bin_charset: MY_CS_PRIMARY,MYF(0)))) { my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), "ucs2"); - YYABORT; + MYSQL_YYABORT; } } | charset charset_name { Lex->charset=$2; } ; @@ -4486,7 +4946,7 @@ key_type: #else my_error(ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name, sym_group_geom.needed_define); - YYABORT; + MYSQL_YYABORT; #endif }; @@ -4519,7 +4979,7 @@ opt_unique_or_fulltext: #else my_error(ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name, sym_group_geom.needed_define); - YYABORT; + MYSQL_YYABORT; #endif } ; @@ -4567,7 +5027,7 @@ key_opt: else { my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), $3.str); - YYABORT; + MYSQL_YYABORT; } } ; @@ -4618,17 +5078,17 @@ alter: { THD *thd= YYTHD; LEX *lex= thd->lex; - lex->name= 0; + lex->name.str= 0; + lex->name.length= 0; lex->sql_command= SQLCOM_ALTER_TABLE; lex->duplicates= DUP_ERROR; if (!lex->select_lex.add_table_to_list(thd, $4, NULL, TL_OPTION_UPDATING)) - YYABORT; + MYSQL_YYABORT; lex->create_list.empty(); lex->key_list.empty(); lex->col_list.empty(); lex->select_lex.init_order(); - lex->name= 0; lex->like_name= 0; lex->select_lex.db= ((TABLE_LIST*) lex->select_lex.table_list.first)->db; @@ -4639,6 +5099,7 @@ alter: lex->alter_info.reset(); lex->alter_info.flags= 0; lex->no_write_to_binlog= 0; + lex->create_info.storage_media= HA_SM_DEFAULT; } alter_commands {} @@ -4649,12 +5110,13 @@ alter: } opt_create_database_options { - LEX *lex=Lex; - THD *thd= Lex->thd; + THD *thd= YYTHD; + LEX *lex= thd->lex; lex->sql_command=SQLCOM_ALTER_DB; lex->name= $3; - if (lex->name == NULL && thd->copy_db_to(&lex->name, NULL)) - YYABORT; + if (lex->name.str == NULL && + thd->copy_db_to(&lex->name.str, &lex->name.length)) + MYSQL_YYABORT; } | ALTER PROCEDURE sp_name { @@ -4663,7 +5125,7 @@ alter: if (lex->sphead) { my_error(ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE"); - YYABORT; + MYSQL_YYABORT; } bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); } @@ -4681,7 +5143,7 @@ alter: if (lex->sphead) { my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"); - YYABORT; + MYSQL_YYABORT; } bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); } @@ -4704,7 +5166,7 @@ alter: } view_list_opt AS view_select view_check_option {} - | ALTER EVENT_SYM sp_name + | ALTER definer EVENT_SYM sp_name /* BE CAREFUL when you add a new rule to update the block where YYTHD->client_capabilities is set back to original value @@ -4719,8 +5181,8 @@ alter: */ if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD))) - YYABORT; - Lex->event_parse_data->identifier= $3; + MYSQL_YYABORT; + Lex->event_parse_data->identifier= $4; /* We have to turn off CLIENT_MULTI_QUERIES while parsing a @@ -4731,8 +5193,6 @@ alter: YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; Lex->sql_command= SQLCOM_ALTER_EVENT; - /* we need that for disallowing subqueries */ - Lex->expr_allows_subselect= FALSE; } ev_alter_on_schedule_completion opt_ev_rename_to @@ -4742,30 +5202,30 @@ alter: { /* $1 - ALTER - $2 - EVENT_SYM - $3 - sp_name - $4 - the block above + $2 - definer + $3 - EVENT_SYM + $4 - sp_name + $5 - the block above */ - YYTHD->client_capabilities |= $<ulong_num>4; + YYTHD->client_capabilities |= $<ulong_num>5; - if (!($5 || $6 || $7 || $8 || $9)) + if (!($6 || $7 || $8 || $9 || $10)) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } /* sql_command is set here because some rules in ev_sql_stmt can overwrite it */ Lex->sql_command= SQLCOM_ALTER_EVENT; - Lex->expr_allows_subselect= TRUE; } | ALTER TABLESPACE alter_tablespace_info { LEX *lex= Lex; lex->alter_tablespace_info->ts_cmd_type= ALTER_TABLESPACE; } - | ALTER LOGFILE_SYM GROUP alter_logfile_group_info + | ALTER LOGFILE_SYM GROUP_SYM alter_logfile_group_info { LEX *lex= Lex; lex->alter_tablespace_info->ts_cmd_type= ALTER_LOGFILE_GROUP; @@ -4780,6 +5240,13 @@ alter: LEX *lex= Lex; lex->alter_tablespace_info->ts_cmd_type= ALTER_ACCESS_MODE_TABLESPACE; } + | ALTER SERVER_SYM ident_or_text OPTIONS_SYM '(' server_options_list ')' + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_ALTER_SERVER; + lex->server_options.server_name= $3.str; + lex->server_options.server_name_length= $3.length; + } ; ev_alter_on_schedule_completion: /* empty */ { $$= 0;} @@ -4804,10 +5271,9 @@ opt_ev_sql_stmt: /* empty*/ { $$= 0;} | DO_SYM ev_sql_stmt { $$= 1; } ; - ident_or_empty: - /* empty */ { $$= 0; } - | ident { $$= $1.str; }; + /* empty */ { $$.str= 0; $$.length= 0; } + | ident { $$= $1; }; alter_commands: | DISCARD TABLESPACE { Lex->alter_info.tablespace_op= DISCARD_TABLESPACE; } @@ -4819,7 +5285,7 @@ alter_commands: | remove_partitioning | partitioning /* - This part was added for release 5.1 by Mikael Ronström. + This part was added for release 5.1 by Mikael Ronström. From here we insert a number of commands to manage the partitions of a partitioned table such as adding partitions, dropping partitions, reorganising partitions in various manners. In future releases the list @@ -4905,7 +5371,7 @@ add_partition_rule: if (!lex->part_info) { mem_alloc_error(sizeof(partition_info)); - YYABORT; + MYSQL_YYABORT; } lex->alter_info.flags|= ALTER_ADD_PARTITION; lex->no_write_to_binlog= $3; @@ -4935,7 +5401,7 @@ reorg_partition_rule: if (!lex->part_info) { mem_alloc_error(sizeof(partition_info)); - YYABORT; + MYSQL_YYABORT; } lex->no_write_to_binlog= $3; } @@ -4970,7 +5436,7 @@ alt_part_name_item: if (Lex->alter_info.partition_names.push_back($1.str)) { mem_alloc_error(1); - YYABORT; + MYSQL_YYABORT; } } ; @@ -5028,7 +5494,7 @@ alter_list_item: &lex->comment, $3.str, &lex->interval_list, lex->charset, lex->uint_geom_type)) - YYABORT; + MYSQL_YYABORT; } opt_place | DROP opt_column field_ident opt_restrict @@ -5083,21 +5549,22 @@ alter_list_item: } | RENAME opt_to table_ident { - LEX *lex=Lex; - THD *thd= lex->thd; + THD *thd= YYTHD; + LEX *lex= thd->lex; + uint dummy; lex->select_lex.db=$3->db.str; if (lex->select_lex.db == NULL && - thd->copy_db_to(&lex->select_lex.db, NULL)) + thd->copy_db_to(&lex->select_lex.db, &dummy)) { - YYABORT; + MYSQL_YYABORT; } if (check_table_name($3->table.str,$3->table.length) || - $3->db.str && check_db_name($3->db.str)) + $3->db.str && check_db_name(&$3->db)) { my_error(ER_WRONG_TABLE_NAME, MYF(0), $3->table.str); - YYABORT; + MYSQL_YYABORT; } - lex->name= $3->table.str; + lex->name= $3->table; lex->alter_info.flags|= ALTER_RENAME; } | CONVERT_SYM TO_SYM charset charset_name_or_default opt_collate @@ -5112,7 +5579,7 @@ alter_list_item: { my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), $5->name, $4->csname); - YYABORT; + MYSQL_YYABORT; } LEX *lex= Lex; lex->create_info.table_charset= @@ -5130,7 +5597,7 @@ alter_list_item: { Lex->alter_info.flags|= ALTER_FORCE; } - | order_clause + | alter_order_clause { LEX *lex=Lex; lex->alter_info.flags|= ALTER_ORDER; @@ -5250,7 +5717,7 @@ slave_until: { my_message(ER_BAD_SLAVE_UNTIL_COND, ER(ER_BAD_SLAVE_UNTIL_COND), MYF(0)); - YYABORT; + MYSQL_YYABORT; } } @@ -5354,7 +5821,7 @@ check: if (lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "CHECK"); - YYABORT; + MYSQL_YYABORT; } lex->sql_command = SQLCOM_CHECK; lex->check_opt.init(); @@ -5421,12 +5888,12 @@ rename_list: user TO_SYM user { if (Lex->users_list.push_back($1) || Lex->users_list.push_back($3)) - YYABORT; + MYSQL_YYABORT; } | rename_list ',' user TO_SYM user { if (Lex->users_list.push_back($3) || Lex->users_list.push_back($5)) - YYABORT; + MYSQL_YYABORT; } ; @@ -5443,18 +5910,18 @@ table_to_table: TL_IGNORE) || !sl->add_table_to_list(lex->thd, $3,NULL,TL_OPTION_UPDATING, TL_IGNORE)) - YYABORT; + MYSQL_YYABORT; }; db_to_db: ident TO_SYM ident { LEX *lex=Lex; - if (Lex->db_list.push_back((LEX_STRING*) + if (lex->db_list.push_back((LEX_STRING*) sql_memdup(&$1, sizeof(LEX_STRING))) || - Lex->db_list.push_back((LEX_STRING*) + lex->db_list.push_back((LEX_STRING*) sql_memdup(&$3, sizeof(LEX_STRING)))) - YYABORT; + MYSQL_YYABORT; }; keycache: @@ -5479,7 +5946,7 @@ assign_to_keycache: TL_READ, sel->get_use_index(), (List<String> *)0)) - YYABORT; + MYSQL_YYABORT; } ; @@ -5511,7 +5978,7 @@ preload_keys: TL_READ, sel->get_use_index(), (List<String> *)0)) - YYABORT; + MYSQL_YYABORT; } ; @@ -5566,16 +6033,16 @@ select_paren: SELECT_LEX * sel= lex->current_select; if (sel->set_braces(1)) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } if (sel->linkage == UNION_TYPE && !sel->master_unit()->first_select()->braces && sel->master_unit()->first_select()->linkage == UNION_TYPE) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } /* select in braces, can't contain global parameters */ if (sel->master_unit()->fake_select_lex) @@ -5591,14 +6058,14 @@ select_init2: SELECT_LEX * sel= lex->current_select; if (lex->current_select->set_braces(0)) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } if (sel->linkage == UNION_TYPE && sel->master_unit()->first_select()->braces) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } } union_clause @@ -5626,8 +6093,13 @@ select_into: | select_from into; select_from: - FROM join_table_list where_clause group_clause having_clause + FROM join_table_list where_clause group_clause having_clause opt_order_clause opt_limit_clause procedure_clause + { + Select->context.table_list= + Select->context.first_name_resolution_table= + (TABLE_LIST *) Select->table_list.first; + } | FROM DUAL_SYM where_clause opt_limit_clause /* oracle compatibility: oracle always requires FROM clause, and DUAL is system table without fields. @@ -5642,7 +6114,7 @@ select_options: if (Select->options & SELECT_DISTINCT && Select->options & SELECT_ALL) { my_error(ER_WRONG_USAGE, MYF(0), "ALL", "DISTINCT"); - YYABORT; + MYSQL_YYABORT; } } ; @@ -5656,7 +6128,7 @@ select_option: | HIGH_PRIORITY { if (check_simple_select()) - YYABORT; + MYSQL_YYABORT; Lex->lock_option= TL_READ_HIGH_PRIORITY; } | DISTINCT { Select->options|= SELECT_DISTINCT; } @@ -5665,13 +6137,13 @@ select_option: | SQL_BUFFER_RESULT { if (check_simple_select()) - YYABORT; + MYSQL_YYABORT; Select->options|= OPTION_BUFFER_RESULT; } | SQL_CALC_FOUND_ROWS { if (check_simple_select()) - YYABORT; + MYSQL_YYABORT; Select->options|= OPTION_FOUND_ROWS; } | SQL_NO_CACHE_SYM @@ -5720,7 +6192,7 @@ select_item_list: new Item_field(&thd->lex->current_select-> context, NULL, NULL, "*"))) - YYABORT; + MYSQL_YYABORT; (thd->lex->current_select->with_wild)++; }; @@ -5729,7 +6201,7 @@ select_item: remember_name select_item2 remember_end select_alias { if (add_item_to_list(YYTHD, $2)) - YYABORT; + MYSQL_YYABORT; if ($4.str) { $2->is_autogenerated_name= FALSE; @@ -5820,13 +6292,18 @@ bool_factor: | bool_test ; bool_test: - bool_pri IS TRUE_SYM { $$= is_truth_value($1,1,0); } - | bool_pri IS not TRUE_SYM { $$= is_truth_value($1,0,0); } - | bool_pri IS FALSE_SYM { $$= is_truth_value($1,0,1); } - | bool_pri IS not FALSE_SYM { $$= is_truth_value($1,1,1); } - | bool_pri IS UNKNOWN_SYM { $$= new Item_func_isnull($1); } - | bool_pri IS not UNKNOWN_SYM { $$= new Item_func_isnotnull($1); } - | bool_pri ; + bool_pri IS TRUE_SYM + { $$= new (YYTHD->mem_root) Item_func_istrue($1); } + | bool_pri IS not TRUE_SYM + { $$= new (YYTHD->mem_root) Item_func_isnottrue($1); } + | bool_pri IS FALSE_SYM + { $$= new (YYTHD->mem_root) Item_func_isfalse($1); } + | bool_pri IS not FALSE_SYM + { $$= new (YYTHD->mem_root) Item_func_isnotfalse($1); } + | bool_pri IS UNKNOWN_SYM { $$= new Item_func_isnull($1); } + | bool_pri IS not UNKNOWN_SYM { $$= new Item_func_isnotnull($1); } + | bool_pri + ; bool_pri: bool_pri IS NULL_SYM { $$= new Item_func_isnull($1); } @@ -5839,31 +6316,37 @@ bool_pri: | predicate ; predicate: - bit_expr IN_SYM '(' subselect ')' - { $$= new Item_in_subselect($1, $4); } - | bit_expr not IN_SYM '(' subselect ')' - { $$= negate_expression(YYTHD, new Item_in_subselect($1, $5)); } + bit_expr IN_SYM '(' subselect ')' + { + $$= new (YYTHD->mem_root) Item_in_subselect($1, $4); + } + | bit_expr not IN_SYM '(' subselect ')' + { + THD *thd= YYTHD; + Item *item= new (thd->mem_root) Item_in_subselect($1, $5); + $$= negate_expression(thd, item); + } | bit_expr IN_SYM '(' expr ')' { - $$= new Item_func_eq($1, $4); + $$= handle_sql2003_note184_exception(YYTHD, $1, true, $4); } - | bit_expr IN_SYM '(' expr ',' expr_list ')' - { - $6->push_front($4); - $6->push_front($1); - $$= new Item_func_in(*$6); + | bit_expr IN_SYM '(' expr ',' expr_list ')' + { + $6->push_front($4); + $6->push_front($1); + $$= new (YYTHD->mem_root) Item_func_in(*$6); } | bit_expr not IN_SYM '(' expr ')' { - $$= new Item_func_ne($1, $5); + $$= handle_sql2003_note184_exception(YYTHD, $1, false, $5); } - | bit_expr not IN_SYM '(' expr ',' expr_list ')' + | bit_expr not IN_SYM '(' expr ',' expr_list ')' { - $7->push_front($5); - $7->push_front($1); - Item_func_in *item = new Item_func_in(*$7); - item->negate(); - $$= item; + $7->push_front($5); + $7->push_front($1); + Item_func_in *item = new (YYTHD->mem_root) Item_func_in(*$7); + item->negate(); + $$= item; } | bit_expr BETWEEN_SYM bit_expr AND_SYM predicate { $$= new Item_func_between($1,$3,$5); } @@ -5944,100 +6427,86 @@ interval_expr: simple_expr: simple_ident + | function_call_keyword + | function_call_nonkeyword + | function_call_generic + | function_call_conflict | simple_expr COLLATE_SYM ident_or_text %prec NEG { - $$= new Item_func_set_collation($1, - new Item_string($3.str, - $3.length, - YYTHD->charset())); + THD *thd= YYTHD; + Item *i1= new (thd->mem_root) Item_string($3.str, + $3.length, + thd->charset()); + $$= new (thd->mem_root) Item_func_set_collation($1, i1); } | literal | param_marker - | '@' ident_or_text SET_VAR expr - { - $$= new Item_func_set_user_var($2,$4); - LEX *lex= Lex; - lex->uncacheable(UNCACHEABLE_RAND); - lex->variables_used= 1; - } - | '@' ident_or_text - { - $$= new Item_func_get_user_var($2); - LEX *lex= Lex; - lex->uncacheable(UNCACHEABLE_RAND); - lex->variables_used= 1; - } - | '@' '@' opt_var_ident_type ident_or_text opt_component - { - - if ($4.str && $5.str && check_reserved_words(&$4)) - { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; - } - if (!($$= get_system_var(YYTHD, $3, $4, $5))) - YYABORT; - Lex->variables_used= 1; - } + | variable | sum_expr | simple_expr OR_OR_SYM simple_expr - { $$= new Item_func_concat($1, $3); } + { $$= new (YYTHD->mem_root) Item_func_concat($1, $3); } | '+' simple_expr %prec NEG { $$= $2; } - | '-' simple_expr %prec NEG { $$= new Item_func_neg($2); } - | '~' simple_expr %prec NEG { $$= new Item_func_bit_neg($2); } - | not2 simple_expr %prec NEG { $$= negate_expression(YYTHD, $2); } + | '-' simple_expr %prec NEG + { $$= new (YYTHD->mem_root) Item_func_neg($2); } + | '~' simple_expr %prec NEG + { $$= new (YYTHD->mem_root) Item_func_bit_neg($2); } + | not2 simple_expr %prec NEG + { $$= negate_expression(YYTHD, $2); } | '(' subselect ')' { - $$= new Item_singlerow_subselect($2); + $$= new (YYTHD->mem_root) Item_singlerow_subselect($2); } | '(' expr ')' { $$= $2; } | '(' expr ',' expr_list ')' { $4->push_front($2); - $$= new Item_row(*$4); + $$= new (YYTHD->mem_root) Item_row(*$4); } | ROW_SYM '(' expr ',' expr_list ')' { $5->push_front($3); - $$= new Item_row(*$5); + $$= new (YYTHD->mem_root) Item_row(*$5); } | EXISTS '(' subselect ')' { - $$= new Item_exists_subselect($3); + $$= new (YYTHD->mem_root) Item_exists_subselect($3); } | '{' ident expr '}' { $$= $3; } | MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')' - { $2->push_front($5); - Select->add_ftfunc_to_list((Item_func_match*) - ($$=new Item_func_match(*$2,$6))); } - | ASCII_SYM '(' expr ')' { $$= new Item_func_ascii($3); } + { + $2->push_front($5); + Item_func_match *i1= new (YYTHD->mem_root) Item_func_match(*$2, $6); + Select->add_ftfunc_to_list(i1); + $$= i1; + } | BINARY simple_expr %prec NEG { - $$= create_func_cast($2, ITEM_CAST_CHAR, -1, 0, &my_charset_bin); + $$= create_func_cast(YYTHD, $2, ITEM_CAST_CHAR, -1, 0, + &my_charset_bin); } | CAST_SYM '(' expr AS cast_type ')' { LEX *lex= Lex; - $$= create_func_cast($3, $5, + $$= create_func_cast(YYTHD, $3, $5, lex->length ? atoi(lex->length) : -1, lex->dec ? atoi(lex->dec) : 0, lex->charset); if (!$$) - YYABORT; + MYSQL_YYABORT; } - | CASE_SYM opt_expr WHEN_SYM when_list opt_else END - { $$= new Item_func_case(* $4, $2, $5 ); } + | CASE_SYM opt_expr when_list opt_else END + { $$= new (YYTHD->mem_root) Item_func_case(* $3, $2, $4 ); } | CONVERT_SYM '(' expr ',' cast_type ')' { - $$= create_func_cast($3, $5, + $$= create_func_cast(YYTHD, $3, $5, Lex->length ? atoi(Lex->length) : -1, Lex->dec ? atoi(Lex->dec) : 0, Lex->charset); if (!$$) - YYABORT; + MYSQL_YYABORT; } | CONVERT_SYM '(' expr USING charset_name ')' - { $$= new Item_func_conv_charset($3,$5); } + { $$= new (YYTHD->mem_root) Item_func_conv_charset($3,$5); } | DEFAULT '(' simple_ident ')' { if ($3->is_splocal()) @@ -6045,547 +6514,429 @@ simple_expr: Item_splocal *il= static_cast<Item_splocal *>($3); my_error(ER_WRONG_COLUMN_NAME, MYF(0), il->my_name()->str); - YYABORT; + MYSQL_YYABORT; } - $$= new Item_default_value(Lex->current_context(), $3); + $$= new (YYTHD->mem_root) Item_default_value(Lex->current_context(), + $3); } | VALUES '(' simple_ident_nospvar ')' - { $$= new Item_insert_value(Lex->current_context(), $3); } - | FUNC_ARG0 '(' ')' - { - if (!$1.symbol->create_func) - { - my_error(ER_FEATURE_DISABLED, MYF(0), - $1.symbol->group->name, - $1.symbol->group->needed_define); - YYABORT; - } - $$= ((Item*(*)(void))($1.symbol->create_func))(); - } - | FUNC_ARG1 '(' expr ')' - { - if (!$1.symbol->create_func) - { - my_error(ER_FEATURE_DISABLED, MYF(0), - $1.symbol->group->name, - $1.symbol->group->needed_define); - YYABORT; - } - $$= ((Item*(*)(Item*))($1.symbol->create_func))($3); - } - | FUNC_ARG2 '(' expr ',' expr ')' - { - if (!$1.symbol->create_func) - { - my_error(ER_FEATURE_DISABLED, MYF(0), - $1.symbol->group->name, - $1.symbol->group->needed_define); - YYABORT; - } - $$= ((Item*(*)(Item*,Item*))($1.symbol->create_func))($3,$5); - } - | FUNC_ARG3 '(' expr ',' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_insert_value(Lex->current_context(), + $3); } + | interval_expr interval '+' expr + /* we cannot put interval before - */ + { $$= new (YYTHD->mem_root) Item_date_add_interval($4,$1,$2,0); } + | interval_expr { - if (!$1.symbol->create_func) - { - my_error(ER_FEATURE_DISABLED, MYF(0), - $1.symbol->group->name, - $1.symbol->group->needed_define); - YYABORT; - } - $$= ((Item*(*)(Item*,Item*,Item*))($1.symbol->create_func))($3,$5,$7); - } - | ADDDATE_SYM '(' expr ',' expr ')' - { $$= new Item_date_add_interval($3, $5, INTERVAL_DAY, 0);} - | ADDDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')' - { $$= new Item_date_add_interval($3, $6, $7, 0); } - | REPEAT_SYM '(' expr ',' expr ')' - { $$= new Item_func_repeat($3,$5); } - | ATAN '(' expr ')' - { $$= new Item_func_atan($3); } - | ATAN '(' expr ',' expr ')' - { $$= new Item_func_atan($3,$5); } - | CHAR_SYM '(' expr_list ')' - { $$= new Item_func_char(*$3); } + if ($1->type() != Item::ROW_ITEM) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } + $$= new (YYTHD->mem_root) Item_func_interval((Item_row *)$1); + } + ; + +/* + Function call syntax using official SQL 2003 keywords. + Because the function name is an official token, + a dedicated grammar rule is needed in the parser. + There is no potential for conflicts +*/ +function_call_keyword: + CHAR_SYM '(' expr_list ')' + { $$= new (YYTHD->mem_root) Item_func_char(*$3); } | CHAR_SYM '(' expr_list USING charset_name ')' - { $$= new Item_func_char(*$3, $5); } - | CHARSET '(' expr ')' - { $$= new Item_func_charset($3); } - | COALESCE '(' expr_list ')' - { $$= new Item_func_coalesce(* $3); } - | COLLATION_SYM '(' expr ')' - { $$= new Item_func_collation($3); } - | CONCAT '(' expr_list ')' - { $$= new Item_func_concat(* $3); } - | CONCAT_WS '(' expr ',' expr_list ')' - { $5->push_front($3); $$= new Item_func_concat_ws(*$5); } - | CONVERT_TZ_SYM '(' expr ',' expr ',' expr ')' - { - if (Lex->add_time_zone_tables_to_query_tables(YYTHD)) - YYABORT; - $$= new Item_func_convert_tz($3, $5, $7); - } - | CURDATE optional_braces - { $$= new Item_func_curdate_local(); Lex->safe_to_cache_query=0; } - | CURTIME optional_braces - { $$= new Item_func_curtime_local(); Lex->safe_to_cache_query=0; } - | CURTIME '(' expr ')' - { - $$= new Item_func_curtime_local($3); - Lex->safe_to_cache_query=0; - } + { $$= new (YYTHD->mem_root) Item_func_char(*$3, $5); } | CURRENT_USER optional_braces { - $$= new Item_func_current_user(Lex->current_context()); + $$= new (YYTHD->mem_root) Item_func_current_user(Lex->current_context()); Lex->safe_to_cache_query= 0; } - | DATE_ADD_INTERVAL '(' expr ',' interval_expr interval ')' - { $$= new Item_date_add_interval($3,$5,$6,0); } - | DATE_SUB_INTERVAL '(' expr ',' interval_expr interval ')' - { $$= new Item_date_add_interval($3,$5,$6,1); } - | DATABASE '(' ')' - { - $$= new Item_func_database(); - Lex->safe_to_cache_query=0; - } | DATE_SYM '(' expr ')' - { $$= new Item_date_typecast($3); } + { $$= new (YYTHD->mem_root) Item_date_typecast($3); } | DAY_SYM '(' expr ')' - { $$= new Item_func_dayofmonth($3); } - | ELT_FUNC '(' expr ',' expr_list ')' - { $5->push_front($3); $$= new Item_func_elt(*$5); } - | MAKE_SET_SYM '(' expr ',' expr_list ')' - { $$= new Item_func_make_set($3, *$5); } - | ENCRYPT '(' expr ')' - { - $$= new Item_func_encrypt($3); - Lex->uncacheable(UNCACHEABLE_RAND); - } - | ENCRYPT '(' expr ',' expr ')' { $$= new Item_func_encrypt($3,$5); } - | DECODE_SYM '(' expr ',' TEXT_STRING_literal ')' - { $$= new Item_func_decode($3,$5.str); } - | ENCODE_SYM '(' expr ',' TEXT_STRING_literal ')' - { $$= new Item_func_encode($3,$5.str); } - | DES_DECRYPT_SYM '(' expr ')' - { $$= new Item_func_des_decrypt($3); } - | DES_DECRYPT_SYM '(' expr ',' expr ')' - { $$= new Item_func_des_decrypt($3,$5); } - | DES_ENCRYPT_SYM '(' expr ')' - { $$= new Item_func_des_encrypt($3); } - | DES_ENCRYPT_SYM '(' expr ',' expr ')' - { $$= new Item_func_des_encrypt($3,$5); } - | EXPORT_SET '(' expr ',' expr ',' expr ')' - { $$= new Item_func_export_set($3, $5, $7); } - | EXPORT_SET '(' expr ',' expr ',' expr ',' expr ')' - { $$= new Item_func_export_set($3, $5, $7, $9); } - | EXPORT_SET '(' expr ',' expr ',' expr ',' expr ',' expr ')' - { $$= new Item_func_export_set($3, $5, $7, $9, $11); } - | FORMAT_SYM '(' expr ',' NUM ')' - { $$= new Item_func_format($3,atoi($5.str)); } - | FROM_UNIXTIME '(' expr ')' - { $$= new Item_func_from_unixtime($3); } - | FROM_UNIXTIME '(' expr ',' expr ')' - { - $$= new Item_func_date_format (new Item_func_from_unixtime($3),$5,0); - } - | FIELD_FUNC '(' expr ',' expr_list ')' - { $5->push_front($3); $$= new Item_func_field(*$5); } - | geometry_function - { -#ifdef HAVE_SPATIAL - $$= $1; -#else - my_error(ER_FEATURE_DISABLED, MYF(0), - sym_group_geom.name, sym_group_geom.needed_define); - YYABORT; -#endif - } - | GET_FORMAT '(' date_time_type ',' expr ')' - { $$= new Item_func_get_format($3, $5); } + { $$= new (YYTHD->mem_root) Item_func_dayofmonth($3); } | HOUR_SYM '(' expr ')' - { $$= new Item_func_hour($3); } - | IF '(' expr ',' expr ',' expr ')' - { $$= new Item_func_if($3,$5,$7); } + { $$= new (YYTHD->mem_root) Item_func_hour($3); } | INSERT '(' expr ',' expr ',' expr ',' expr ')' - { $$= new Item_func_insert($3,$5,$7,$9); } - | interval_expr interval '+' expr - /* we cannot put interval before - */ - { $$= new Item_date_add_interval($4,$1,$2,0); } - | interval_expr + { $$= new (YYTHD->mem_root) Item_func_insert($3,$5,$7,$9); } + | LEFT '(' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_left($3,$5); } + | MINUTE_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_minute($3); } + | MONTH_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_month($3); } + | RIGHT '(' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_right($3,$5); } + | SECOND_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_second($3); } + | TIME_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_time_typecast($3); } + | TIMESTAMP '(' expr ')' + { $$= new (YYTHD->mem_root) Item_datetime_typecast($3); } + | TIMESTAMP '(' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_add_time($3, $5, 1, 0); } + | TRIM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_trim($3); } + | TRIM '(' LEADING expr FROM expr ')' + { $$= new (YYTHD->mem_root) Item_func_ltrim($6,$4); } + | TRIM '(' TRAILING expr FROM expr ')' + { $$= new (YYTHD->mem_root) Item_func_rtrim($6,$4); } + | TRIM '(' BOTH expr FROM expr ')' + { $$= new (YYTHD->mem_root) Item_func_trim($6,$4); } + | TRIM '(' LEADING FROM expr ')' + { $$= new (YYTHD->mem_root) Item_func_ltrim($5); } + | TRIM '(' TRAILING FROM expr ')' + { $$= new (YYTHD->mem_root) Item_func_rtrim($5); } + | TRIM '(' BOTH FROM expr ')' + { $$= new (YYTHD->mem_root) Item_func_trim($5); } + | TRIM '(' expr FROM expr ')' + { $$= new (YYTHD->mem_root) Item_func_trim($5,$3); } + | USER '(' ')' { - if ($1->type() != Item::ROW_ITEM) - { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; - } - $$= new Item_func_interval((Item_row *)$1); + $$= new (YYTHD->mem_root) Item_func_user(); + Lex->safe_to_cache_query=0; } - | LAST_INSERT_ID '(' ')' + | YEAR_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_year($3); } + ; + +/* + Function calls using non reserved keywords, with special syntaxic forms. + Dedicated grammar rules are needed because of the syntax, + but also have the potential to cause incompatibilities with other + parts of the language. + MAINTAINER: + The only reasons a function should be added here are: + - for compatibility reasons with another SQL syntax (CURDATE), + - for typing reasons (GET_FORMAT) + Any other 'Syntaxic sugar' enhancements should be *STRONGLY* + discouraged. +*/ +function_call_nonkeyword: + ADDDATE_SYM '(' expr ',' expr ')' { - $$= new Item_func_last_insert_id(); - Lex->safe_to_cache_query= 0; - } - | LAST_INSERT_ID '(' expr ')' + $$= new (YYTHD->mem_root) Item_date_add_interval($3, $5, + INTERVAL_DAY, 0); + } + | ADDDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')' + { $$= new (YYTHD->mem_root) Item_date_add_interval($3, $6, $7, 0); } + | CURDATE optional_braces { - $$= new Item_func_last_insert_id($3); - Lex->safe_to_cache_query= 0; - } - | LEFT '(' expr ',' expr ')' - { $$= new Item_func_left($3,$5); } - | LOCATE '(' expr ',' expr ')' - { $$= new Item_func_locate($5,$3); } - | LOCATE '(' expr ',' expr ',' expr ')' - { $$= new Item_func_locate($5,$3,$7); } - | GREATEST_SYM '(' expr ',' expr_list ')' - { $5->push_front($3); $$= new Item_func_max(*$5); } - | LEAST_SYM '(' expr ',' expr_list ')' - { $5->push_front($3); $$= new Item_func_min(*$5); } - | LOG_SYM '(' expr ')' - { $$= new Item_func_log($3); } - | LOG_SYM '(' expr ',' expr ')' - { $$= new Item_func_log($3, $5); } - | MASTER_POS_WAIT '(' expr ',' expr ')' - { - $$= new Item_master_pos_wait($3, $5); - Lex->safe_to_cache_query=0; - } - | MASTER_POS_WAIT '(' expr ',' expr ',' expr ')' + $$= new (YYTHD->mem_root) Item_func_curdate_local(); + Lex->safe_to_cache_query=0; + } + | CURTIME optional_braces { - $$= new Item_master_pos_wait($3, $5, $7); + $$= new (YYTHD->mem_root) Item_func_curtime_local(); + Lex->safe_to_cache_query=0; + } + | CURTIME '(' expr ')' + { + $$= new (YYTHD->mem_root) Item_func_curtime_local($3); Lex->safe_to_cache_query=0; } - | MICROSECOND_SYM '(' expr ')' - { $$= new Item_func_microsecond($3); } - | MINUTE_SYM '(' expr ')' - { $$= new Item_func_minute($3); } - | MOD_SYM '(' expr ',' expr ')' - { $$ = new Item_func_mod( $3, $5); } - | MONTH_SYM '(' expr ')' - { $$= new Item_func_month($3); } + | DATE_ADD_INTERVAL '(' expr ',' interval_expr interval ')' + { $$= new (YYTHD->mem_root) Item_date_add_interval($3,$5,$6,0); } + | DATE_SUB_INTERVAL '(' expr ',' interval_expr interval ')' + { $$= new (YYTHD->mem_root) Item_date_add_interval($3,$5,$6,1); } + | EXTRACT_SYM '(' interval FROM expr ')' + { $$=new (YYTHD->mem_root) Item_extract( $3, $5); } + | GET_FORMAT '(' date_time_type ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_get_format($3, $5); } | NOW_SYM optional_braces - { $$= new Item_func_now_local(); Lex->safe_to_cache_query=0;} + { + $$= new (YYTHD->mem_root) Item_func_now_local(); + Lex->safe_to_cache_query=0; + } | NOW_SYM '(' expr ')' - { $$= new Item_func_now_local($3); Lex->safe_to_cache_query=0;} - | PASSWORD '(' expr ')' { - $$= YYTHD->variables.old_passwords ? - (Item *) new Item_func_old_password($3) : - (Item *) new Item_func_password($3); - } - | OLD_PASSWORD '(' expr ')' - { $$= new Item_func_old_password($3); } + $$= new (YYTHD->mem_root) Item_func_now_local($3); + Lex->safe_to_cache_query=0; + } | POSITION_SYM '(' bit_expr IN_SYM expr ')' - { $$ = new Item_func_locate($5,$3); } - | QUARTER_SYM '(' expr ')' - { $$ = new Item_func_quarter($3); } - | RAND '(' expr ')' - { $$= new Item_func_rand($3); Lex->uncacheable(UNCACHEABLE_RAND);} - | RAND '(' ')' - { $$= new Item_func_rand(); Lex->uncacheable(UNCACHEABLE_RAND);} - | REPLACE '(' expr ',' expr ',' expr ')' - { $$= new Item_func_replace($3,$5,$7); } - | RIGHT '(' expr ',' expr ')' - { $$= new Item_func_right($3,$5); } - | ROUND '(' expr ')' - { $$= new Item_func_round($3, new Item_int((char*)"0",0,1),0); } - | ROUND '(' expr ',' expr ')' { $$= new Item_func_round($3,$5,0); } - | ROW_COUNT_SYM '(' ')' - { - $$= new Item_func_row_count(); - Lex->safe_to_cache_query= 0; - } + { $$ = new (YYTHD->mem_root) Item_func_locate($5,$3); } | SUBDATE_SYM '(' expr ',' expr ')' - { $$= new Item_date_add_interval($3, $5, INTERVAL_DAY, 1);} + { + $$= new (YYTHD->mem_root) Item_date_add_interval($3, $5, + INTERVAL_DAY, 1); + } | SUBDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')' - { $$= new Item_date_add_interval($3, $6, $7, 1); } - | SECOND_SYM '(' expr ')' - { $$= new Item_func_second($3); } + { $$= new (YYTHD->mem_root) Item_date_add_interval($3, $6, $7, 1); } | SUBSTRING '(' expr ',' expr ',' expr ')' - { $$= new Item_func_substr($3,$5,$7); } + { $$= new (YYTHD->mem_root) Item_func_substr($3,$5,$7); } | SUBSTRING '(' expr ',' expr ')' - { $$= new Item_func_substr($3,$5); } + { $$= new (YYTHD->mem_root) Item_func_substr($3,$5); } | SUBSTRING '(' expr FROM expr FOR_SYM expr ')' - { $$= new Item_func_substr($3,$5,$7); } + { $$= new (YYTHD->mem_root) Item_func_substr($3,$5,$7); } | SUBSTRING '(' expr FROM expr ')' - { $$= new Item_func_substr($3,$5); } - | SUBSTRING_INDEX '(' expr ',' expr ',' expr ')' - { $$= new Item_func_substr_index($3,$5,$7); } + { $$= new (YYTHD->mem_root) Item_func_substr($3,$5); } | SYSDATE optional_braces { if (global_system_variables.sysdate_is_now == 0) - $$= new Item_func_sysdate_local(); - else $$= new Item_func_now_local(); + $$= new (YYTHD->mem_root) Item_func_sysdate_local(); + else + $$= new (YYTHD->mem_root) Item_func_now_local(); Lex->safe_to_cache_query=0; } | SYSDATE '(' expr ')' { if (global_system_variables.sysdate_is_now == 0) - $$= new Item_func_sysdate_local($3); - else $$= new Item_func_now_local($3); + $$= new (YYTHD->mem_root) Item_func_sysdate_local($3); + else + $$= new (YYTHD->mem_root) Item_func_now_local($3); Lex->safe_to_cache_query=0; } - | TIME_SYM '(' expr ')' - { $$= new Item_time_typecast($3); } - | TIMESTAMP '(' expr ')' - { $$= new Item_datetime_typecast($3); } - | TIMESTAMP '(' expr ',' expr ')' - { $$= new Item_func_add_time($3, $5, 1, 0); } | TIMESTAMP_ADD '(' interval_time_st ',' expr ',' expr ')' - { $$= new Item_date_add_interval($7,$5,$3,0); } + { $$= new (YYTHD->mem_root) Item_date_add_interval($7,$5,$3,0); } | TIMESTAMP_DIFF '(' interval_time_st ',' expr ',' expr ')' - { $$= new Item_func_timestamp_diff($5,$7,$3); } - | TRIM '(' expr ')' - { $$= new Item_func_trim($3); } - | TRIM '(' LEADING expr FROM expr ')' - { $$= new Item_func_ltrim($6,$4); } - | TRIM '(' TRAILING expr FROM expr ')' - { $$= new Item_func_rtrim($6,$4); } - | TRIM '(' BOTH expr FROM expr ')' - { $$= new Item_func_trim($6,$4); } - | TRIM '(' LEADING FROM expr ')' - { $$= new Item_func_ltrim($5); } - | TRIM '(' TRAILING FROM expr ')' - { $$= new Item_func_rtrim($5); } - | TRIM '(' BOTH FROM expr ')' - { $$= new Item_func_trim($5); } - | TRIM '(' expr FROM expr ')' - { $$= new Item_func_trim($5,$3); } - | TRUNCATE_SYM '(' expr ',' expr ')' - { $$= new Item_func_round($3,$5,1); } - | ident '.' ident '(' udf_expr_list ')' + { $$= new (YYTHD->mem_root) Item_func_timestamp_diff($5,$7,$3); } + | UTC_DATE_SYM optional_braces { - LEX *lex= Lex; - sp_name *name= new sp_name($1, $3); + $$= new (YYTHD->mem_root) Item_func_curdate_utc(); + Lex->safe_to_cache_query=0; + } + | UTC_TIME_SYM optional_braces + { + $$= new (YYTHD->mem_root) Item_func_curtime_utc(); + Lex->safe_to_cache_query=0; + } + | UTC_TIMESTAMP_SYM optional_braces + { + $$= new (YYTHD->mem_root) Item_func_now_utc(); + Lex->safe_to_cache_query=0; + } + ; - name->init_qname(YYTHD); - sp_add_used_routine(lex, YYTHD, name, TYPE_ENUM_FUNCTION); - if ($5) - $$= new Item_func_sp(Lex->current_context(), name, *$5); - else - $$= new Item_func_sp(Lex->current_context(), name); - lex->safe_to_cache_query=0; +/* + Functions calls using a non reserved keyword, and using a regular syntax. + Because the non reserved keyword is used in another part of the grammar, + a dedicated rule is needed here. +*/ +function_call_conflict: + ASCII_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_ascii($3); } + | CHARSET '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_charset($3); } + | COALESCE '(' expr_list ')' + { $$= new (YYTHD->mem_root) Item_func_coalesce(* $3); } + | COLLATION_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_collation($3); } + | DATABASE '(' ')' + { + $$= new (YYTHD->mem_root) Item_func_database(); + Lex->safe_to_cache_query=0; } - | IDENT_sys '(' + | IF '(' expr ',' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_if($3,$5,$7); } + | MICROSECOND_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_microsecond($3); } + | MOD_SYM '(' expr ',' expr ')' + { $$ = new (YYTHD->mem_root) Item_func_mod( $3, $5); } + | OLD_PASSWORD '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_old_password($3); } + | PASSWORD '(' expr ')' + { + THD *thd= YYTHD; + Item* i1; + if (thd->variables.old_passwords) + i1= new (thd->mem_root) Item_func_old_password($3); + else + i1= new (thd->mem_root) Item_func_password($3); + $$= i1; + } + | QUARTER_SYM '(' expr ')' + { $$ = new (YYTHD->mem_root) Item_func_quarter($3); } + | REPEAT_SYM '(' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_repeat($3,$5); } + | REPLACE '(' expr ',' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_replace($3,$5,$7); } + | TRUNCATE_SYM '(' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_round($3,$5,1); } + | WEEK_SYM '(' expr ')' + { + THD *thd= YYTHD; + Item *i1= new (thd->mem_root) Item_int((char*) "0", + thd->variables.default_week_format, + 1); + + $$= new (thd->mem_root) Item_func_week($3, i1); + } + | WEEK_SYM '(' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_week($3,$5); } + | geometry_function { +#ifdef HAVE_SPATIAL + $$= $1; +#else + my_error(ER_FEATURE_DISABLED, MYF(0), + sym_group_geom.name, sym_group_geom.needed_define); + MYSQL_YYABORT; +#endif + } + ; + +geometry_function: + CONTAINS_SYM '(' expr ',' expr ')' + { + $$= GEOM_NEW(YYTHD, + Item_func_spatial_rel($3, $5, + Item_func::SP_CONTAINS_FUNC)); + } + | GEOMETRYCOLLECTION '(' expr_list ')' + { + $$= GEOM_NEW(YYTHD, + Item_func_spatial_collection(* $3, + Geometry::wkb_geometrycollection, + Geometry::wkb_point)); + } + | LINESTRING '(' expr_list ')' + { + $$= GEOM_NEW(YYTHD, + Item_func_spatial_collection(* $3, + Geometry::wkb_linestring, + Geometry::wkb_point)); + } + | MULTILINESTRING '(' expr_list ')' + { + $$= GEOM_NEW(YYTHD, + Item_func_spatial_collection(* $3, + Geometry::wkb_multilinestring, + Geometry::wkb_linestring)); + } + | MULTIPOINT '(' expr_list ')' + { + $$= GEOM_NEW(YYTHD, + Item_func_spatial_collection(* $3, + Geometry::wkb_multipoint, + Geometry::wkb_point)); + } + | MULTIPOLYGON '(' expr_list ')' + { + $$= GEOM_NEW(YYTHD, + Item_func_spatial_collection(* $3, + Geometry::wkb_multipolygon, + Geometry::wkb_polygon)); + } + | POINT_SYM '(' expr ',' expr ')' + { $$= GEOM_NEW(YYTHD, Item_func_point($3,$5)); } + | POLYGON '(' expr_list ')' + { + $$= GEOM_NEW(YYTHD, + Item_func_spatial_collection(* $3, + Geometry::wkb_polygon, + Geometry::wkb_linestring)); + } + ; + +/* + Regular function calls. + The function name is *not* a token, and therefore is guaranteed to not + introduce side effects to the language in general. + MAINTAINER: + All the new functions implemented for new features should fit into + this category. The place to implement the function itself is + in sql/item_create.cc +*/ +function_call_generic: + IDENT_sys '(' + { #ifdef HAVE_DLOPEN - udf_func *udf= 0; - if (using_udf_functions && - (udf= find_udf($1.str, $1.length)) && - udf->type == UDFTYPE_AGGREGATE) + udf_func *udf= 0; + LEX *lex= Lex; + if (using_udf_functions && + (udf= find_udf($1.str, $1.length)) && + udf->type == UDFTYPE_AGGREGATE) + { + if (lex->current_select->inc_in_sum_expr()) { - LEX *lex= Lex; - if (lex->current_select->inc_in_sum_expr()) - { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; - } + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } - $<udf>$= udf; + } + /* Temporary placing the result of find_udf in $3 */ + $<udf>$= udf; #endif + } + udf_expr_list ')' + { + THD *thd= YYTHD; + Create_func *builder; + Item *item= NULL; + + /* + Implementation note: + names are resolved with the following order: + - MySQL native functions, + - User Defined Functions, + - Stored Functions (assuming the current <use> database) + + This will be revised with WL#2128 (SQL PATH) + */ + builder= find_native_function_builder(thd, $1); + if (builder) + { + item= builder->create(thd, $1, $4); } - udf_expr_list ')' + else { #ifdef HAVE_DLOPEN + /* Retrieving the result of find_udf */ udf_func *udf= $<udf>3; - SELECT_LEX *sel= Select; if (udf) { if (udf->type == UDFTYPE_AGGREGATE) + { Select->in_sum_expr--; - - Lex->binlog_row_based_if_mixed= TRUE; - - switch (udf->returns) { - case STRING_RESULT: - if (udf->type == UDFTYPE_FUNCTION) - { - if ($4 != NULL) - $$ = new Item_func_udf_str(udf, *$4); - else - $$ = new Item_func_udf_str(udf); - } - else - { - if ($4 != NULL) - $$ = new Item_sum_udf_str(udf, *$4); - else - $$ = new Item_sum_udf_str(udf); - } - break; - case REAL_RESULT: - if (udf->type == UDFTYPE_FUNCTION) - { - if ($4 != NULL) - $$ = new Item_func_udf_float(udf, *$4); - else - $$ = new Item_func_udf_float(udf); - } - else - { - if ($4 != NULL) - $$ = new Item_sum_udf_float(udf, *$4); - else - $$ = new Item_sum_udf_float(udf); - } - break; - case INT_RESULT: - if (udf->type == UDFTYPE_FUNCTION) - { - if ($4 != NULL) - $$ = new Item_func_udf_int(udf, *$4); - else - $$ = new Item_func_udf_int(udf); - } - else - { - if ($4 != NULL) - $$ = new Item_sum_udf_int(udf, *$4); - else - $$ = new Item_sum_udf_int(udf); - } - break; - case DECIMAL_RESULT: - if (udf->type == UDFTYPE_FUNCTION) - { - if ($4 != NULL) - $$ = new Item_func_udf_decimal(udf, *$4); - else - $$ = new Item_func_udf_decimal(udf); - } - else - { - if ($4 != NULL) - $$ = new Item_sum_udf_decimal(udf, *$4); - else - $$ = new Item_sum_udf_decimal(udf); - } - break; - default: - YYABORT; } + + item= Create_udf_func::s_singleton.create(thd, udf, $4); } else -#endif /* HAVE_DLOPEN */ +#endif { - LEX *lex= Lex; - THD *thd= lex->thd; - LEX_STRING db; - if (thd->copy_db_to(&db.str, &db.length)) - YYABORT; - sp_name *name= new sp_name(db, $1); - if (name) - name->init_qname(thd); - - sp_add_used_routine(lex, YYTHD, name, TYPE_ENUM_FUNCTION); - if ($4) - $$= new Item_func_sp(Lex->current_context(), name, *$4); - else - $$= new Item_func_sp(Lex->current_context(), name); - lex->safe_to_cache_query=0; - } + builder= find_qualified_function_builder(thd); + DBUG_ASSERT(builder); + item= builder->create(thd, $1, $4); + } } - | UNIQUE_USERS '(' text_literal ',' NUM ',' NUM ',' expr_list ')' - { - $$= new Item_func_unique_users($3,atoi($5.str),atoi($7.str), * $9); - } - | UNIX_TIMESTAMP '(' ')' - { - $$= new Item_func_unix_timestamp(); - Lex->safe_to_cache_query=0; - } - | UNIX_TIMESTAMP '(' expr ')' - { $$= new Item_func_unix_timestamp($3); } - | USER '(' ')' - { $$= new Item_func_user(); Lex->safe_to_cache_query=0; } - | UTC_DATE_SYM optional_braces - { $$= new Item_func_curdate_utc(); Lex->safe_to_cache_query=0;} - | UTC_TIME_SYM optional_braces - { $$= new Item_func_curtime_utc(); Lex->safe_to_cache_query=0;} - | UTC_TIMESTAMP_SYM optional_braces - { $$= new Item_func_now_utc(); Lex->safe_to_cache_query=0;} - | WEEK_SYM '(' expr ')' - { - $$= new Item_func_week($3,new Item_int((char*) "0", - YYTHD->variables.default_week_format,1)); + + if (! ($$= item)) + { + MYSQL_YYABORT; } - | WEEK_SYM '(' expr ',' expr ')' - { $$= new Item_func_week($3,$5); } - | YEAR_SYM '(' expr ')' - { $$= new Item_func_year($3); } - | YEARWEEK '(' expr ')' - { $$= new Item_func_yearweek($3,new Item_int((char*) "0",0,1)); } - | YEARWEEK '(' expr ',' expr ')' - { $$= new Item_func_yearweek($3, $5); } - | BENCHMARK_SYM '(' ulong_num ',' expr ')' - { - $$=new Item_func_benchmark($3,$5); - Lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - } - | EXTRACT_SYM '(' interval FROM expr ')' - { $$=new Item_extract( $3, $5); }; + } + | ident '.' ident '(' opt_expr_list ')' + { + THD *thd= YYTHD; + Create_qfunc *builder; + Item *item= NULL; -geometry_function: - CONTAINS_SYM '(' expr ',' expr ')' - { $$= GEOM_NEW(Item_func_spatial_rel($3, $5, Item_func::SP_CONTAINS_FUNC)); } - | GEOMFROMTEXT '(' expr ')' - { $$= GEOM_NEW(Item_func_geometry_from_text($3)); } - | GEOMFROMTEXT '(' expr ',' expr ')' - { $$= GEOM_NEW(Item_func_geometry_from_text($3, $5)); } - | GEOMFROMWKB '(' expr ')' - { $$= GEOM_NEW(Item_func_geometry_from_wkb($3)); } - | GEOMFROMWKB '(' expr ',' expr ')' - { $$= GEOM_NEW(Item_func_geometry_from_wkb($3, $5)); } - | GEOMETRYCOLLECTION '(' expr_list ')' - { $$= GEOM_NEW(Item_func_spatial_collection(* $3, - Geometry::wkb_geometrycollection, - Geometry::wkb_point)); } - | LINESTRING '(' expr_list ')' - { $$= GEOM_NEW(Item_func_spatial_collection(* $3, - Geometry::wkb_linestring, Geometry::wkb_point)); } - | MULTILINESTRING '(' expr_list ')' - { $$= GEOM_NEW( Item_func_spatial_collection(* $3, - Geometry::wkb_multilinestring, Geometry::wkb_linestring)); } - | MLINEFROMTEXT '(' expr ')' - { $$= GEOM_NEW(Item_func_geometry_from_text($3)); } - | MLINEFROMTEXT '(' expr ',' expr ')' - { $$= GEOM_NEW(Item_func_geometry_from_text($3, $5)); } - | MPOINTFROMTEXT '(' expr ')' - { $$= GEOM_NEW(Item_func_geometry_from_text($3)); } - | MPOINTFROMTEXT '(' expr ',' expr ')' - { $$= GEOM_NEW(Item_func_geometry_from_text($3, $5)); } - | MPOLYFROMTEXT '(' expr ')' - { $$= GEOM_NEW(Item_func_geometry_from_text($3)); } - | MPOLYFROMTEXT '(' expr ',' expr ')' - { $$= GEOM_NEW(Item_func_geometry_from_text($3, $5)); } - | MULTIPOINT '(' expr_list ')' - { $$= GEOM_NEW(Item_func_spatial_collection(* $3, - Geometry::wkb_multipoint, Geometry::wkb_point)); } - | MULTIPOLYGON '(' expr_list ')' - { $$= GEOM_NEW(Item_func_spatial_collection(* $3, - Geometry::wkb_multipolygon, Geometry::wkb_polygon)); } - | POINT_SYM '(' expr ',' expr ')' - { $$= GEOM_NEW(Item_func_point($3,$5)); } - | POINTFROMTEXT '(' expr ')' - { $$= GEOM_NEW(Item_func_geometry_from_text($3)); } - | POINTFROMTEXT '(' expr ',' expr ')' - { $$= GEOM_NEW(Item_func_geometry_from_text($3, $5)); } - | POLYFROMTEXT '(' expr ')' - { $$= GEOM_NEW(Item_func_geometry_from_text($3)); } - | POLYFROMTEXT '(' expr ',' expr ')' - { $$= GEOM_NEW(Item_func_geometry_from_text($3, $5)); } - | POLYGON '(' expr_list ')' - { $$= GEOM_NEW(Item_func_spatial_collection(* $3, - Geometry::wkb_polygon, Geometry::wkb_linestring)); } - | GEOMCOLLFROMTEXT '(' expr ')' - { $$= GEOM_NEW(Item_func_geometry_from_text($3)); } - | GEOMCOLLFROMTEXT '(' expr ',' expr ')' - { $$= GEOM_NEW(Item_func_geometry_from_text($3, $5)); } - | LINEFROMTEXT '(' expr ')' - { $$= GEOM_NEW(Item_func_geometry_from_text($3)); } - | LINEFROMTEXT '(' expr ',' expr ')' - { $$= GEOM_NEW(Item_func_geometry_from_text($3, $5)); } - ; + /* + The following in practice calls: + <code>Create_sp_func::create()</code> + and builds a stored function. + + However, it's important to maintain the interface between the + parser and the implementation in item_create.cc clean, + since this will change with WL#2128 (SQL PATH): + - INFORMATION_SCHEMA.version() is the SQL 99 syntax for the native + function version(), + - MySQL.version() is the SQL 2003 syntax for the native function + version() (a vendor can specify any schema). + */ + + builder= find_qualified_function_builder(thd); + DBUG_ASSERT(builder); + item= builder->create(thd, $1, $3, $5); + + if (! ($$= item)) + { + MYSQL_YYABORT; + } + } + ; fulltext_options: opt_natural_language_mode opt_query_expansion @@ -6629,6 +6980,12 @@ udf_expr_list3: udf_expr: remember_name expr remember_end select_alias { + /* + Use Item::name as a storage for the attribute value of user + defined function argument. It is safe to use Item::name + because the syntax will not allow having an explicit name here. + See WL#1017 re. udf attributes. + */ if ($4.str) { $2->is_autogenerated_name= FALSE; @@ -6661,13 +7018,11 @@ sum_expr: { Select->in_sum_expr--; } ')' { $$=new Item_sum_count_distinct(* $5); } - | GROUP_UNIQUE_USERS '(' text_literal ',' NUM ',' NUM ',' in_sum_expr ')' - { $$= new Item_sum_unique_users($3,atoi($5.str),atoi($7.str),$9); } | MIN_SYM '(' in_sum_expr ')' { $$=new Item_sum_min($3); } /* According to ANSI SQL, DISTINCT is allowed and has - no sence inside MIN and MAX grouping functions; so MIN|MAX(DISTINCT ...) + no sense inside MIN and MAX grouping functions; so MIN|MAX(DISTINCT ...) is processed like an ordinary MIN | MAX() */ | MIN_SYM '(' DISTINCT in_sum_expr ')' @@ -6701,6 +7056,46 @@ sum_expr: $5->empty(); }; +variable: + '@' + { + if (! Lex->parsing_options.allows_variable) + { + my_error(ER_VIEW_SELECT_VARIABLE, MYF(0)); + MYSQL_YYABORT; + } + } + variable_aux + { + $$= $3; + } + ; + +variable_aux: + ident_or_text SET_VAR expr + { + $$= new Item_func_set_user_var($1, $3); + LEX *lex= Lex; + lex->uncacheable(UNCACHEABLE_RAND); + } + | ident_or_text + { + $$= new Item_func_get_user_var($1); + LEX *lex= Lex; + lex->uncacheable(UNCACHEABLE_RAND); + } + | '@' opt_var_ident_type ident_or_text opt_component + { + if ($3.str && $4.str && check_reserved_words(&$3)) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } + if (!($$= get_system_var(YYTHD, $2, $3, $4))) + MYSQL_YYABORT; + } + ; + opt_distinct: /* empty */ { $$ = 0; } |DISTINCT { $$ = 1; }; @@ -6731,8 +7126,8 @@ in_sum_expr: LEX *lex= Lex; if (lex->current_select->inc_in_sum_expr()) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } } expr @@ -6755,6 +7150,11 @@ cast_type: | DECIMAL_SYM float_options { $$=ITEM_CAST_DECIMAL; Lex->charset= NULL; } ; +opt_expr_list: + /* empty */ { $$= NULL; } + | expr_list { $$= $1;} + ; + expr_list: { Select->expr_list.push_front(new List<Item>); } expr_list2 @@ -6786,37 +7186,33 @@ opt_else: | ELSE expr { $$= $2; }; when_list: - { Select->when_list.push_front(new List<Item>); } - when_list2 - { $$= Select->when_list.pop(); }; - -when_list2: - expr THEN_SYM expr - { - SELECT_LEX *sel=Select; - sel->when_list.head()->push_back($1); - sel->when_list.head()->push_back($3); - } - | when_list2 WHEN_SYM expr THEN_SYM expr - { - SELECT_LEX *sel=Select; - sel->when_list.head()->push_back($3); - sel->when_list.head()->push_back($5); - }; + WHEN_SYM expr THEN_SYM expr + { + $$= new List<Item>; + $$->push_back($2); + $$->push_back($4); + } + | when_list WHEN_SYM expr THEN_SYM expr + { + $1->push_back($3); + $1->push_back($5); + $$= $1; + } + ; /* Warning - may return NULL in case of incomplete SELECT */ table_ref: table_factor { $$=$1; } - | join_table { $$=$1; } + | join_table { LEX *lex= Lex; if (!($$= lex->current_select->nest_last_join(lex->thd))) - YYABORT; + MYSQL_YYABORT; } ; join_table_list: - derived_table_list { YYERROR_UNLESS($$=$1); } + derived_table_list { MYSQL_YYABORT_UNLESS($$=$1); } ; /* Warning - may return NULL in case of incomplete SELECT */ @@ -6824,7 +7220,7 @@ derived_table_list: table_ref { $$=$1; } | derived_table_list ',' table_ref { - YYERROR_UNLESS($1 && ($$=$3)); + MYSQL_YYABORT_UNLESS($1 && ($$=$3)); } ; @@ -6843,16 +7239,16 @@ join_table: left-associative joins. */ table_ref %prec TABLE_REF_PRIORITY normal_join table_ref - { YYERROR_UNLESS($1 && ($$=$3)); } + { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); } | table_ref STRAIGHT_JOIN table_factor - { YYERROR_UNLESS($1 && ($$=$3)); $3->straight=1; } + { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); $3->straight=1; } | table_ref normal_join table_ref ON { - YYERROR_UNLESS($1 && ($$=$3)); + MYSQL_YYABORT_UNLESS($1 && $3); /* Change the current name resolution context to a local context. */ if (push_new_name_resolution_context(YYTHD, $1, $3)) - YYABORT; + MYSQL_YYABORT; Select->parsing_place= IN_ON; } expr @@ -6864,10 +7260,10 @@ join_table: | table_ref STRAIGHT_JOIN table_factor ON { - YYERROR_UNLESS($1 && ($$=$3)); + MYSQL_YYABORT_UNLESS($1 && $3); /* Change the current name resolution context to a local context. */ if (push_new_name_resolution_context(YYTHD, $1, $3)) - YYABORT; + MYSQL_YYABORT; Select->parsing_place= IN_ON; } expr @@ -6880,25 +7276,24 @@ join_table: | table_ref normal_join table_ref USING { - SELECT_LEX *sel= Select; - YYERROR_UNLESS($1 && $3); + MYSQL_YYABORT_UNLESS($1 && $3); } '(' using_list ')' - { add_join_natural($1,$3,$7); $$=$3; } + { add_join_natural($1,$3,$7,Select); $$=$3; } | table_ref NATURAL JOIN_SYM table_factor { - YYERROR_UNLESS($1 && ($$=$4)); - add_join_natural($1,$4,NULL); + MYSQL_YYABORT_UNLESS($1 && ($$=$4)); + add_join_natural($1,$4,NULL,Select); } /* LEFT JOIN variants */ | table_ref LEFT opt_outer JOIN_SYM table_ref ON { - YYERROR_UNLESS($1 && $5); + MYSQL_YYABORT_UNLESS($1 && $5); /* Change the current name resolution context to a local context. */ if (push_new_name_resolution_context(YYTHD, $1, $5)) - YYABORT; + MYSQL_YYABORT; Select->parsing_place= IN_ON; } expr @@ -6911,15 +7306,18 @@ join_table: } | table_ref LEFT opt_outer JOIN_SYM table_factor { - SELECT_LEX *sel= Select; - YYERROR_UNLESS($1 && $5); + MYSQL_YYABORT_UNLESS($1 && $5); } USING '(' using_list ')' - { add_join_natural($1,$5,$9); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; } + { + add_join_natural($1,$5,$9,Select); + $5->outer_join|=JOIN_TYPE_LEFT; + $$=$5; + } | table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor { - YYERROR_UNLESS($1 && $6); - add_join_natural($1,$6,NULL); + MYSQL_YYABORT_UNLESS($1 && $6); + add_join_natural($1,$6,NULL,Select); $6->outer_join|=JOIN_TYPE_LEFT; $$=$6; } @@ -6928,40 +7326,39 @@ join_table: | table_ref RIGHT opt_outer JOIN_SYM table_ref ON { - YYERROR_UNLESS($1 && $5); + MYSQL_YYABORT_UNLESS($1 && $5); /* Change the current name resolution context to a local context. */ if (push_new_name_resolution_context(YYTHD, $1, $5)) - YYABORT; + MYSQL_YYABORT; Select->parsing_place= IN_ON; } expr { LEX *lex= Lex; if (!($$= lex->current_select->convert_right_join())) - YYABORT; + MYSQL_YYABORT; add_join_on($$, $8); Lex->pop_context(); Select->parsing_place= NO_MATTER; } | table_ref RIGHT opt_outer JOIN_SYM table_factor { - SELECT_LEX *sel= Select; - YYERROR_UNLESS($1 && $5); + MYSQL_YYABORT_UNLESS($1 && $5); } USING '(' using_list ')' { LEX *lex= Lex; if (!($$= lex->current_select->convert_right_join())) - YYABORT; - add_join_natural($$,$5,$9); + MYSQL_YYABORT; + add_join_natural($$,$5,$9,Select); } | table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor { - YYERROR_UNLESS($1 && $6); - add_join_natural($6,$1,NULL); + MYSQL_YYABORT_UNLESS($1 && $6); + add_join_natural($6,$1,NULL,Select); LEX *lex= Lex; if (!($$= lex->current_select->convert_right_join())) - YYABORT; + MYSQL_YYABORT; }; normal_join: @@ -6986,7 +7383,7 @@ table_factor: lex->lock_option, sel->get_use_index(), sel->get_ignore_index()))) - YYABORT; + MYSQL_YYABORT; sel->add_joined_table($$); } | '{' ident table_ref LEFT OUTER JOIN_SYM table_ref @@ -6994,19 +7391,19 @@ table_factor: { /* Change the current name resolution context to a local context. */ if (push_new_name_resolution_context(YYTHD, $3, $7)) - YYABORT; + MYSQL_YYABORT; } expr '}' { LEX *lex= Lex; - YYERROR_UNLESS($3 && $7); + MYSQL_YYABORT_UNLESS($3 && $7); add_join_on($7,$10); Lex->pop_context(); $7->outer_join|=JOIN_TYPE_LEFT; $$=$7; if (!($$= lex->current_select->nest_last_join(lex->thd))) - YYABORT; + MYSQL_YYABORT; } | select_derived_init get_select_lex select_derived2 { @@ -7016,8 +7413,8 @@ table_factor: { if (sel->set_braces(1)) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } /* select in braces, can't contain global parameters */ if (sel->master_unit()->fake_select_lex) @@ -7025,7 +7422,7 @@ table_factor: sel->master_unit()->fake_select_lex; } if ($2->init_nested_join(lex->thd)) - YYABORT; + MYSQL_YYABORT; $$= 0; /* incomplete derived tables return NULL, we must be nested in select_derived rule to be here. */ @@ -7059,7 +7456,7 @@ table_factor: TL_READ,(List<String> *)0, (List<String> *)0))) - YYABORT; + MYSQL_YYABORT; sel->add_joined_table($$); lex->pop_context(); } @@ -7067,8 +7464,8 @@ table_factor: if ($4 || $6) { /* simple nested joins cannot have aliases or unions */ - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } else $$= $3; @@ -7081,7 +7478,7 @@ select_derived: { LEX *lex= Lex; if ($1->init_nested_join(lex->thd)) - YYABORT; + MYSQL_YYABORT; } derived_table_list { @@ -7090,11 +7487,11 @@ select_derived: for derived tables, both must equal NULL */ if (!($$= $1->end_nested_join(lex->thd)) && $3) - YYABORT; + MYSQL_YYABORT; if (!$3 && $$) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } } ; @@ -7105,12 +7502,12 @@ select_derived2: lex->derived_tables|= DERIVED_SUBQUERY; if (!lex->expr_allows_subselect) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE || mysql_new_select(lex, 1)) - YYABORT; + MYSQL_YYABORT; mysql_init_select(lex); lex->current_select->linkage= DERIVED_TABLE_TYPE; lex->current_select->parsing_place= SELECT_LIST; @@ -7130,13 +7527,20 @@ select_derived_init: SELECT_SYM { LEX *lex= Lex; + + if (! lex->parsing_options.allows_derived) + { + my_error(ER_VIEW_SELECT_DERIVED, MYF(0)); + MYSQL_YYABORT; + } + SELECT_LEX *sel= lex->current_select; TABLE_LIST *embedding; if (!sel->embedding || sel->end_nested_join(lex->thd)) { /* we are not in parentheses */ - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } embedding= Select->embedding; $$= embedding && @@ -7200,7 +7604,7 @@ using_list: ident { if (!($$= new List<String>)) - YYABORT; + MYSQL_YYABORT; $$->push_back(new (YYTHD->mem_root) String((const char *) $1.str, $1.length, system_charset_info)); @@ -7316,13 +7720,13 @@ opt_escape: group_clause: /* empty */ - | GROUP BY group_list olap_opt; + | GROUP_SYM BY group_list olap_opt; group_list: group_list ',' order_ident order_dir - { if (add_group_to_list(YYTHD, $3,(bool) $4)) YYABORT; } + { if (add_group_to_list(YYTHD, $3,(bool) $4)) MYSQL_YYABORT; } | order_ident order_dir - { if (add_group_to_list(YYTHD, $1,(bool) $2)) YYABORT; }; + { if (add_group_to_list(YYTHD, $1,(bool) $2)) MYSQL_YYABORT; }; olap_opt: /* empty */ {} @@ -7333,11 +7737,11 @@ olap_opt: { my_error(ER_WRONG_USAGE, MYF(0), "WITH CUBE", "global union parameters"); - YYABORT; + MYSQL_YYABORT; } lex->current_select->olap= CUBE_TYPE; my_error(ER_NOT_SUPPORTED_YET, MYF(0), "CUBE"); - YYABORT; /* To be deleted in 5.1 */ + MYSQL_YYABORT; /* To be deleted in 5.1 */ } | WITH ROLLUP_SYM { @@ -7346,13 +7750,36 @@ olap_opt: { my_error(ER_WRONG_USAGE, MYF(0), "WITH ROLLUP", "global union parameters"); - YYABORT; + MYSQL_YYABORT; } lex->current_select->olap= ROLLUP_TYPE; } ; /* + Order by statement in ALTER TABLE +*/ + +alter_order_clause: + ORDER_SYM BY alter_order_list + ; + +alter_order_list: + alter_order_list ',' alter_order_item + | alter_order_item + ; + +alter_order_item: + simple_ident_nospvar order_dir + { + THD *thd= YYTHD; + bool ascending= ($2 == 1) ? true : false; + if (add_order_to_list(thd, $1, ascending)) + MYSQL_YYABORT; + } + ; + +/* Order by statement in select */ @@ -7371,7 +7798,7 @@ order_clause: { my_error(ER_WRONG_USAGE, MYF(0), "CUBE/ROLLUP", "ORDER BY"); - YYABORT; + MYSQL_YYABORT; } if (lex->sql_command != SQLCOM_ALTER_TABLE && !unit->fake_select_lex) { @@ -7388,15 +7815,15 @@ order_clause: (first_sl->order_list.elements || first_sl->select_limit) && unit->add_fake_select_lex(lex->thd)) - YYABORT; + MYSQL_YYABORT; } } order_list; order_list: order_list ',' order_ident order_dir - { if (add_order_to_list(YYTHD, $3,(bool) $4)) YYABORT; } + { if (add_order_to_list(YYTHD, $3,(bool) $4)) MYSQL_YYABORT; } | order_ident order_dir - { if (add_order_to_list(YYTHD, $1,(bool) $2)) YYABORT; }; + { if (add_order_to_list(YYTHD, $1,(bool) $2)) MYSQL_YYABORT; }; order_dir: /* empty */ { $$ = 1; } @@ -7481,7 +7908,7 @@ real_ulong_num: | HEX_NUM { $$= (ulong) strtol($1.str, (char**) 0, 16); } | LONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } | ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } - | dec_num_error { YYABORT; } + | dec_num_error { MYSQL_YYABORT; } ; ulonglong_num: @@ -7496,12 +7923,12 @@ real_ulonglong_num: NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } | ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } | LONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } - | dec_num_error { YYABORT; } + | dec_num_error { MYSQL_YYABORT; } ; dec_num_error: dec_num - { yyerror(ER(ER_ONLY_INTEGERS_ALLOWED)); } + { my_parse_error(ER(ER_ONLY_INTEGERS_ALLOWED)); } ; dec_num: @@ -7514,10 +7941,17 @@ procedure_clause: | PROCEDURE ident /* Procedure name */ { LEX *lex=Lex; + + if (! lex->parsing_options.allows_select_procedure) + { + my_error(ER_VIEW_SELECT_CLAUSE, MYF(0), "PROCEDURE"); + MYSQL_YYABORT; + } + if (&lex->select_lex != lex->current_select) { my_error(ER_WRONG_USAGE, MYF(0), "PROCEDURE", "subquery"); - YYABORT; + MYSQL_YYABORT; } lex->proc_list.elements=0; lex->proc_list.first=0; @@ -7526,7 +7960,7 @@ procedure_clause: current_select-> context, NULL,NULL,$2.str))) - YYABORT; + MYSQL_YYABORT; Lex->uncacheable(UNCACHEABLE_SIDEEFFECT); } '(' procedure_list ')'; @@ -7545,7 +7979,7 @@ procedure_item: { LEX *lex= Lex; if (add_proc_to_list(lex->thd, $2)) - YYABORT; + MYSQL_YYABORT; if (!$2->name) $2->set_name($1,(uint) ((char*) lex->tok_end - $1), YYTHD->charset()); @@ -7557,7 +7991,7 @@ select_var_list_init: { LEX *lex=Lex; if (!lex->describe && (!(lex->result= new select_dumpvar()))) - YYABORT; + MYSQL_YYABORT; } select_var_list {} @@ -7589,7 +8023,7 @@ select_var_ident: if (!lex->spcont || !(t=lex->spcont->find_variable(&$1))) { my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } if (lex->result) { @@ -7613,28 +8047,40 @@ select_var_ident: ; into: - INTO OUTFILE TEXT_STRING_filesystem + INTO + { + if (! Lex->parsing_options.allows_select_into) + { + my_error(ER_VIEW_SELECT_CLAUSE, MYF(0), "INTO"); + MYSQL_YYABORT; + } + } + into_destination + ; + +into_destination: + OUTFILE TEXT_STRING_filesystem { LEX *lex= Lex; lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - if (!(lex->exchange= new sql_exchange($3.str, 0)) || + if (!(lex->exchange= new sql_exchange($2.str, 0)) || !(lex->result= new select_export(lex->exchange))) - YYABORT; + MYSQL_YYABORT; } opt_field_term opt_line_term - | INTO DUMPFILE TEXT_STRING_filesystem + | DUMPFILE TEXT_STRING_filesystem { LEX *lex=Lex; if (!lex->describe) { lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - if (!(lex->exchange= new sql_exchange($3.str,1))) - YYABORT; + if (!(lex->exchange= new sql_exchange($2.str,1))) + MYSQL_YYABORT; if (!(lex->result= new select_dump(lex->exchange))) - YYABORT; + MYSQL_YYABORT; } } - | INTO select_var_list_init + | select_var_list_init { Lex->uncacheable(UNCACHEABLE_SIDEEFFECT); } @@ -7677,14 +8123,14 @@ drop: $3.str)); if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL, TL_OPTION_UPDATING)) - YYABORT; + MYSQL_YYABORT; } | DROP DATABASE if_exists ident { LEX *lex=Lex; lex->sql_command= SQLCOM_DROP_DB; lex->drop_if_exists=$3; - lex->name=$4.str; + lex->name= $4; } | DROP FUNCTION_SYM if_exists sp_name { @@ -7692,7 +8138,7 @@ drop: if (lex->sphead) { my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"); - YYABORT; + MYSQL_YYABORT; } lex->sql_command = SQLCOM_DROP_FUNCTION; lex->drop_if_exists= $3; @@ -7704,7 +8150,7 @@ drop: if (lex->sphead) { my_error(ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE"); - YYABORT; + MYSQL_YYABORT; } lex->sql_command = SQLCOM_DROP_PROCEDURE; lex->drop_if_exists= $3; @@ -7726,22 +8172,30 @@ drop: Lex->spname= $4; Lex->sql_command = SQLCOM_DROP_EVENT; } - | DROP TRIGGER_SYM sp_name + | DROP TRIGGER_SYM if_exists sp_name { LEX *lex= Lex; lex->sql_command= SQLCOM_DROP_TRIGGER; - lex->spname= $3; + lex->drop_if_exists= $3; + lex->spname= $4; } | DROP TABLESPACE tablespace_name opt_ts_engine opt_ts_wait { LEX *lex= Lex; lex->alter_tablespace_info->ts_cmd_type= DROP_TABLESPACE; } - | DROP LOGFILE_SYM GROUP logfile_group_name opt_ts_engine opt_ts_wait + | DROP LOGFILE_SYM GROUP_SYM logfile_group_name opt_ts_engine opt_ts_wait { LEX *lex= Lex; lex->alter_tablespace_info->ts_cmd_type= DROP_LOGFILE_GROUP; - } + } + | DROP SERVER_SYM if_exists ident_or_text + { + Lex->sql_command = SQLCOM_DROP_SERVER; + Lex->drop_if_exists= $3; + Lex->server_options.server_name= $4.str; + Lex->server_options.server_name_length= $4.length; + } ; table_list: @@ -7752,7 +8206,7 @@ table_name: table_ident { if (!Select->add_table_to_list(YYTHD, $1, NULL, TL_OPTION_UPDATING)) - YYABORT; + MYSQL_YYABORT; } ; @@ -7850,7 +8304,7 @@ insert_field_spec: LEX *lex=Lex; if (!(lex->insert_list = new List_item) || lex->many_values.push_back(lex->insert_list)) - YYABORT; + MYSQL_YYABORT; } ident_eq_list; @@ -7880,7 +8334,7 @@ ident_eq_value: LEX *lex=Lex; if (lex->field_list.push_back($1) || lex->insert_list->push_back($3)) - YYABORT; + MYSQL_YYABORT; }; equal: EQ {} @@ -7896,13 +8350,13 @@ no_braces: '(' { if (!(Lex->insert_list = new List_item)) - YYABORT; + MYSQL_YYABORT; } opt_values ')' { LEX *lex=Lex; if (lex->many_values.push_back(lex->insert_list)) - YYABORT; + MYSQL_YYABORT; }; opt_values: @@ -7913,12 +8367,12 @@ values: values ',' expr_or_default { if (Lex->insert_list->push_back($3)) - YYABORT; + MYSQL_YYABORT; } | expr_or_default { if (Lex->insert_list->push_back($1)) - YYABORT; + MYSQL_YYABORT; } ; @@ -7955,7 +8409,7 @@ update: /* it is single table update and it is update of derived table */ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), lex->select_lex.get_table_list()->alias, "UPDATE"); - YYABORT; + MYSQL_YYABORT; } /* In case of multi-update setting write lock for all tables may @@ -7975,7 +8429,7 @@ update_elem: simple_ident_nospvar equal expr_or_default { if (add_item_to_list(YYTHD, $1) || add_value_to_list(YYTHD, $3)) - YYABORT; + MYSQL_YYABORT; }; insert_update_list: @@ -7988,7 +8442,7 @@ insert_update_elem: LEX *lex= Lex; if (lex->update_list.push_back($1) || lex->value_list.push_back($3)) - YYABORT; + MYSQL_YYABORT; }; opt_low_priority: @@ -8015,7 +8469,7 @@ single_multi: { if (!Select->add_table_to_list(YYTHD, $2, NULL, TL_OPTION_UPDATING, Lex->lock_option)) - YYABORT; + MYSQL_YYABORT; } where_clause opt_order_clause delete_limit_clause {} @@ -8024,14 +8478,14 @@ single_multi: FROM join_table_list where_clause { if (multi_delete_set_locks_and_link_aux_tables(Lex)) - YYABORT; + MYSQL_YYABORT; } | FROM table_wild_list { mysql_init_multi_delete(Lex); } USING join_table_list where_clause { if (multi_delete_set_locks_and_link_aux_tables(Lex)) - YYABORT; + MYSQL_YYABORT; } ; @@ -8045,7 +8499,7 @@ table_wild_one: if (!Select->add_table_to_list(YYTHD, new Table_ident($1), $3, TL_OPTION_UPDATING | TL_OPTION_ALIAS, Lex->lock_option)) - YYABORT; + MYSQL_YYABORT; } | ident '.' ident opt_wild opt_table_alias { @@ -8055,7 +8509,7 @@ table_wild_one: TL_OPTION_UPDATING | TL_OPTION_ALIAS, Lex->lock_option)) - YYABORT; + MYSQL_YYABORT; } ; @@ -8109,7 +8563,7 @@ show_param: LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_DATABASES; if (prepare_schema_table(YYTHD, lex, 0, SCH_SCHEMATA)) - YYABORT; + MYSQL_YYABORT; } | opt_full TABLES opt_db wild_and_where { @@ -8117,7 +8571,7 @@ show_param: lex->sql_command= SQLCOM_SHOW_TABLES; lex->select_lex.db= $3; if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_NAMES)) - YYABORT; + MYSQL_YYABORT; } | opt_full TRIGGERS_SYM opt_db wild_and_where { @@ -8125,7 +8579,7 @@ show_param: lex->sql_command= SQLCOM_SHOW_TRIGGERS; lex->select_lex.db= $3; if (prepare_schema_table(YYTHD, lex, 0, SCH_TRIGGERS)) - YYABORT; + MYSQL_YYABORT; } | EVENTS_SYM opt_db wild_and_where { @@ -8133,7 +8587,7 @@ show_param: lex->sql_command= SQLCOM_SHOW_EVENTS; lex->select_lex.db= $2; if (prepare_schema_table(YYTHD, lex, 0, SCH_EVENTS)) - YYABORT; + MYSQL_YYABORT; } | TABLE_SYM STATUS_SYM opt_db wild_and_where { @@ -8141,7 +8595,7 @@ show_param: lex->sql_command= SQLCOM_SHOW_TABLE_STATUS; lex->select_lex.db= $3; if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLES)) - YYABORT; + MYSQL_YYABORT; } | OPEN_SYM TABLES opt_db wild_and_where { @@ -8149,7 +8603,7 @@ show_param: lex->sql_command= SQLCOM_SHOW_OPEN_TABLES; lex->select_lex.db= $3; if (prepare_schema_table(YYTHD, lex, 0, SCH_OPEN_TABLES)) - YYABORT; + MYSQL_YYABORT; } | opt_full PLUGIN_SYM { @@ -8157,21 +8611,19 @@ show_param: WARN_DEPRECATED(yythd, "5.2", "SHOW PLUGIN", "'SHOW PLUGINS'"); lex->sql_command= SQLCOM_SHOW_PLUGINS; if (prepare_schema_table(YYTHD, lex, 0, SCH_PLUGINS)) - YYABORT; + MYSQL_YYABORT; } | PLUGINS_SYM { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_PLUGINS; if (prepare_schema_table(YYTHD, lex, 0, SCH_PLUGINS)) - YYABORT; + MYSQL_YYABORT; } - | ENGINE_SYM storage_engines + | ENGINE_SYM known_storage_engines show_engine_param { Lex->create_info.db_type= $2; } - show_engine_param - | ENGINE_SYM ALL + | ENGINE_SYM ALL show_engine_param { Lex->create_info.db_type= NULL; } - show_engine_param | opt_full COLUMNS from_or_in table_ident opt_db wild_and_where { LEX *lex= Lex; @@ -8179,7 +8631,7 @@ show_param: if ($5) $4->change_db($5); if (prepare_schema_table(YYTHD, lex, $4, SCH_COLUMNS)) - YYABORT; + MYSQL_YYABORT; } | NEW_SYM MASTER_SYM FOR_SYM SLAVE WITH MASTER_LOG_FILE_SYM EQ TEXT_STRING_sys AND_SYM MASTER_LOG_POS_SYM EQ ulonglong_num @@ -8211,7 +8663,7 @@ show_param: if ($4) $3->change_db($4); if (prepare_schema_table(YYTHD, lex, $3, SCH_STATISTICS)) - YYABORT; + MYSQL_YYABORT; } | COLUMN_SYM TYPES_SYM { @@ -8229,7 +8681,7 @@ show_param: LEX *lex=Lex; lex->sql_command= SQLCOM_SHOW_STORAGE_ENGINES; if (prepare_schema_table(YYTHD, lex, 0, SCH_ENGINES)) - YYABORT; + MYSQL_YYABORT; } | AUTHORS_SYM { @@ -8260,7 +8712,7 @@ show_param: lex->sql_command= SQLCOM_SHOW_STATUS; lex->option_type= $1; if (prepare_schema_table(YYTHD, lex, 0, SCH_STATUS)) - YYABORT; + MYSQL_YYABORT; } | INNOBASE_SYM STATUS_SYM { @@ -8270,7 +8722,7 @@ show_param: ha_resolve_by_legacy_type(YYTHD, DB_TYPE_INNODB))) { my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), "InnoDB"); - YYABORT; + MYSQL_YYABORT; } WARN_DEPRECATED(yythd, "5.2", "SHOW INNODB STATUS", "'SHOW ENGINE INNODB STATUS'"); } @@ -8282,7 +8734,7 @@ show_param: ha_resolve_by_legacy_type(YYTHD, DB_TYPE_INNODB))) { my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), "InnoDB"); - YYABORT; + MYSQL_YYABORT; } WARN_DEPRECATED(yythd, "5.2", "SHOW MUTEX STATUS", "'SHOW ENGINE INNODB MUTEX'"); } @@ -8294,21 +8746,21 @@ show_param: lex->sql_command= SQLCOM_SHOW_VARIABLES; lex->option_type= $1; if (prepare_schema_table(YYTHD, lex, 0, SCH_VARIABLES)) - YYABORT; + MYSQL_YYABORT; } | charset wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_CHARSETS; if (prepare_schema_table(YYTHD, lex, 0, SCH_CHARSETS)) - YYABORT; + MYSQL_YYABORT; } | COLLATION_SYM wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_COLLATIONS; if (prepare_schema_table(YYTHD, lex, 0, SCH_COLLATIONS)) - YYABORT; + MYSQL_YYABORT; } | GRANTS { @@ -8316,7 +8768,7 @@ show_param: lex->sql_command= SQLCOM_SHOW_GRANTS; LEX_USER *curr_user; if (!(curr_user= (LEX_USER*) lex->thd->alloc(sizeof(st_lex_user)))) - YYABORT; + MYSQL_YYABORT; bzero(curr_user, sizeof(st_lex_user)); lex->grant_user= curr_user; } @@ -8331,22 +8783,23 @@ show_param: { Lex->sql_command=SQLCOM_SHOW_CREATE_DB; Lex->create_info.options=$3; - Lex->name=$4.str; + Lex->name= $4; } | CREATE TABLE_SYM table_ident { LEX *lex= Lex; lex->sql_command = SQLCOM_SHOW_CREATE; if (!lex->select_lex.add_table_to_list(YYTHD, $3, NULL,0)) - YYABORT; + MYSQL_YYABORT; lex->only_view= 0; + lex->create_info.storage_media= HA_SM_DEFAULT; } | CREATE VIEW_SYM table_ident { LEX *lex= Lex; lex->sql_command = SQLCOM_SHOW_CREATE; if (!lex->select_lex.add_table_to_list(YYTHD, $3, NULL, 0)) - YYABORT; + MYSQL_YYABORT; lex->only_view= 1; } | MASTER_SYM STATUS_SYM @@ -8376,24 +8829,24 @@ show_param: LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_STATUS_PROC; if (!sp_add_to_query_tables(YYTHD, lex, "mysql", "proc", TL_READ)) - YYABORT; + MYSQL_YYABORT; if (prepare_schema_table(YYTHD, lex, 0, SCH_PROCEDURES)) - YYABORT; + MYSQL_YYABORT; } | FUNCTION_SYM STATUS_SYM wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_STATUS_FUNC; if (!sp_add_to_query_tables(YYTHD, lex, "mysql", "proc", TL_READ)) - YYABORT; + MYSQL_YYABORT; if (prepare_schema_table(YYTHD, lex, 0, SCH_PROCEDURES)) - YYABORT; + MYSQL_YYABORT; } | PROCEDURE CODE_SYM sp_name { #ifdef DBUG_OFF - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; #else Lex->sql_command= SQLCOM_SHOW_PROC_CODE; Lex->spname= $3; @@ -8402,8 +8855,8 @@ show_param: | FUNCTION_SYM CODE_SYM sp_name { #ifdef DBUG_OFF - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; #else Lex->sql_command= SQLCOM_SHOW_FUNC_CODE; Lex->spname= $3; @@ -8478,7 +8931,7 @@ describe: lex->select_lex.db= 0; lex->verbose= 0; if (prepare_schema_table(YYTHD, lex, $2, SCH_COLUMNS)) - YYABORT; + MYSQL_YYABORT; } opt_describe_column {} | describe_command opt_extended_describe @@ -8592,17 +9045,12 @@ purge_option: /* kill threads */ kill: - KILL_SYM - { - Lex->sql_command= SQLCOM_KILL; - Lex->expr_allows_subselect= FALSE; - } - kill_option expr + KILL_SYM kill_option expr { LEX *lex=Lex; lex->value_list.empty(); - lex->value_list.push_front($4); - Lex->expr_allows_subselect= TRUE; + lex->value_list.push_front($3); + lex->sql_command= SQLCOM_KILL; }; kill_option: @@ -8628,7 +9076,7 @@ load: LOAD DATA_SYM if (lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD DATA"); - YYABORT; + MYSQL_YYABORT; } lex->fname_start= lex->ptr; } @@ -8643,11 +9091,11 @@ load: LOAD DATA_SYM if (lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD TABLE"); - YYABORT; + MYSQL_YYABORT; } lex->sql_command = SQLCOM_LOAD_MASTER_TABLE; if (!Select->add_table_to_list(YYTHD, $3, NULL, TL_OPTION_UPDATING)) - YYABORT; + MYSQL_YYABORT; }; load_data: @@ -8660,7 +9108,7 @@ load_data: lex->duplicates= DUP_ERROR; lex->ignore= 0; if (!(lex->exchange= new sql_exchange($4.str, 0))) - YYABORT; + MYSQL_YYABORT; } opt_duplicate INTO { @@ -8672,11 +9120,13 @@ load_data: LEX *lex=Lex; if (!Select->add_table_to_list(YYTHD, $10, NULL, TL_OPTION_UPDATING, lex->lock_option)) - YYABORT; + MYSQL_YYABORT; lex->field_list.empty(); lex->update_list.empty(); lex->value_list.empty(); } + opt_load_data_charset + { Lex->exchange->cs= $12; } opt_field_term opt_line_term opt_ignore_lines opt_field_or_var_spec opt_load_data_set_spec {} @@ -8702,6 +9152,8 @@ load_data_lock: Ignore this option in SP to avoid problem with query cache */ if (Lex->sphead != 0) + $$= YYTHD->update_lock_default; + else #endif $$= TL_WRITE_CONCURRENT_INSERT; } @@ -8841,14 +9293,19 @@ text_string: param_marker: PARAM_MARKER { - THD *thd=YYTHD; + THD *thd= YYTHD; LEX *lex= thd->lex; - Item_param *item= new Item_param((uint) (lex->tok_start - - (uchar *) thd->query)); + Item_param *item; + if (! lex->parsing_options.allows_variable) + { + my_error(ER_VIEW_SELECT_VARIABLE, MYF(0)); + MYSQL_YYABORT; + } + item= new Item_param((uint) (lex->tok_start - (uchar *) thd->query)); if (!($$= item) || lex->param_list.push_back(item)) { my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); - YYABORT; + MYSQL_YYABORT; } } ; @@ -8914,7 +9371,7 @@ NUM_literal: $$= new Item_decimal($1.str, $1.length, YYTHD->charset()); if (YYTHD->net.report_error) { - YYABORT; + MYSQL_YYABORT; } } | FLOAT_NUM @@ -8922,7 +9379,7 @@ NUM_literal: $$ = new Item_float($1.str, $1.length); if (YYTHD->net.report_error) { - YYABORT; + MYSQL_YYABORT; } } ; @@ -8964,6 +9421,12 @@ simple_ident: if (spc && (spv = spc->find_variable(&$1))) { /* We're compiling a stored procedure and found a variable */ + if (! lex->parsing_options.allows_variable) + { + my_error(ER_VIEW_SELECT_VARIABLE, MYF(0)); + MYSQL_YYABORT; + } + Item_splocal *splocal; splocal= new Item_splocal($1, spv->offset, spv->type, lex->tok_start_prev - @@ -8973,7 +9436,6 @@ simple_ident: splocal->m_sp= lex->sphead; #endif $$ = (Item*) splocal; - lex->variables_used= 1; lex->safe_to_cache_query=0; } else @@ -9022,14 +9484,14 @@ simple_ident_q: !new_row) { my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "OLD", "on INSERT"); - YYABORT; + MYSQL_YYABORT; } if (lex->trg_chistics.event == TRG_EVENT_DELETE && new_row) { my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE"); - YYABORT; + MYSQL_YYABORT; } DBUG_ASSERT(!new_row || @@ -9044,7 +9506,7 @@ simple_ident_q: $3.str, SELECT_ACL, read_only))) - YYABORT; + MYSQL_YYABORT; /* Let us add this item to list of all Item_trigger_field objects @@ -9115,13 +9577,13 @@ field_ident: if (my_strcasecmp(table_alias_charset, $1.str, table->db)) { my_error(ER_WRONG_DB_NAME, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } if (my_strcasecmp(table_alias_charset, $3.str, table->table_name)) { my_error(ER_WRONG_TABLE_NAME, MYF(0), $3.str); - YYABORT; + MYSQL_YYABORT; } $$=$5; } @@ -9131,7 +9593,7 @@ field_ident: if (my_strcasecmp(table_alias_charset, $1.str, table->alias)) { my_error(ER_WRONG_TABLE_NAME, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } $$=$3; } @@ -9163,7 +9625,7 @@ IDENT_sys: { my_error(ER_INVALID_CHARACTER_STRING, MYF(0), cs->csname, $1.str + wlen); - YYABORT; + MYSQL_YYABORT; } $$= $1; } @@ -9246,32 +9708,32 @@ user: { THD *thd= YYTHD; if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) - YYABORT; + MYSQL_YYABORT; $$->user = $1; $$->host.str= (char *) "%"; $$->host.length= 1; if (check_string_length(&$$->user, ER(ER_USERNAME), USERNAME_LENGTH)) - YYABORT; + MYSQL_YYABORT; } | ident_or_text '@' ident_or_text { THD *thd= YYTHD; if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) - YYABORT; + MYSQL_YYABORT; $$->user = $1; $$->host=$3; if (check_string_length(&$$->user, ER(ER_USERNAME), USERNAME_LENGTH) || check_string_length(&$$->host, ER(ER_HOSTNAME), HOSTNAME_LENGTH)) - YYABORT; + MYSQL_YYABORT; } | CURRENT_USER optional_braces { if (!($$=(LEX_USER*) YYTHD->alloc(sizeof(st_lex_user)))) - YYABORT; + MYSQL_YYABORT; /* empty LEX_USER means current_user and will be handled in the get_current_user() function @@ -9301,12 +9763,16 @@ keyword: | FLUSH_SYM {} | HANDLER_SYM {} | HELP_SYM {} + | HOST_SYM {} | INSTALL_SYM {} | LANGUAGE_SYM {} | NO_SYM {} | OPEN_SYM {} + | OPTIONS_SYM {} + | OWNER_SYM {} | PARSER_SYM {} | PARTITION_SYM {} + | PORT_SYM {} | PREPARE_SYM {} | REMOVE_SYM {} | REPAIR {} @@ -9315,7 +9781,9 @@ keyword: | ROLLBACK_SYM {} | SAVEPOINT_SYM {} | SECURITY_SYM {} + | SERVER_SYM {} | SIGNED_SYM {} + | SOCKET_SYM {} | SLAVE {} | SONAME_SYM {} | START_SYM {} @@ -9323,6 +9791,7 @@ keyword: | TRUNCATE_SYM {} | UNICODE_SYM {} | UNINSTALL_SYM {} + | WRAPPER_SYM {} | XA_SYM {} | UPGRADE_SYM {} ; @@ -9366,6 +9835,7 @@ keyword_sp: | COMPLETION_SYM {} | COMPRESSED_SYM {} | CONCURRENT {} + | CONNECTION_SYM {} | CONSISTENT_SYM {} | CONTRIBUTORS_SYM {} | CUBE_SYM {} @@ -9652,7 +10122,7 @@ option_type_value: if (!(i= new sp_instr_stmt(sp->instructions(), lex->spcont, lex))) - YYABORT; + MYSQL_YYABORT; /* Extract the query statement from the tokenizer. The @@ -9665,7 +10135,7 @@ option_type_value: qbuff.length= lex->tok_end - sp->m_tmp_query; if (!(qbuff.str= alloc_root(YYTHD->mem_root, qbuff.length + 5))) - YYABORT; + MYSQL_YYABORT; strmake(strmake(qbuff.str, "SET ", 4), (char *)sp->m_tmp_query, qbuff.length); @@ -9721,8 +10191,8 @@ sys_option_value: LINT_INIT(sp_fld); if ($1) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } if ($4) it= $4; @@ -9744,7 +10214,7 @@ sys_option_value: lex->spcont, trg_fld, it, lex))) - YYABORT; + MYSQL_YYABORT; /* Let us add this item to list of all Item_trigger_field @@ -9771,8 +10241,8 @@ sys_option_value: Item *it; if ($1) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } spv= ctx->find_variable(&$2.base_name); @@ -9812,7 +10282,7 @@ option_value: | charset old_or_new_charset_name_or_default { THD *thd= YYTHD; - LEX *lex= Lex; + LEX *lex= thd->lex; $2= $2 ? $2: global_system_variables.character_set_client; lex->var_list.push_back(new set_var_collation_client($2,thd->variables.collation_database,$2)); } @@ -9827,9 +10297,9 @@ option_value: if (spc && spc->find_variable(&names)) my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), names.str); else - yyerror(ER(ER_SYNTAX_ERROR)); + my_parse_error(ER(ER_SYNTAX_ERROR)); - YYABORT; + MYSQL_YYABORT; } | NAMES_SYM charset_name_or_default opt_collate { @@ -9840,15 +10310,15 @@ option_value: { my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), $3->name, $2->csname); - YYABORT; + MYSQL_YYABORT; } lex->var_list.push_back(new set_var_collation_client($3,$3,$3)); } | PASSWORD equal text_or_password { - THD *thd=YYTHD; + THD *thd= YYTHD; + LEX *lex= thd->lex; LEX_USER *user; - LEX *lex= Lex; sp_pcontext *spc= lex->spcont; LEX_STRING pw; @@ -9857,10 +10327,10 @@ option_value: if (spc && spc->find_variable(&pw)) { my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str); - YYABORT; + MYSQL_YYABORT; } if (!(user=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))) - YYABORT; + MYSQL_YYABORT; user->host=null_lex_str; user->user.str=thd->security_ctx->priv_user; thd->lex->var_list.push_back(new set_var_password(user, $3)); @@ -9884,7 +10354,7 @@ internal_variable_name: /* Not an SP local variable */ sys_var *tmp=find_sys_var($1.str, $1.length); if (!tmp) - YYABORT; + MYSQL_YYABORT; $$.var= tmp; $$.base_name= null_lex_str; /* @@ -9893,7 +10363,7 @@ internal_variable_name: */ if (tmp == &sys_time_zone && lex->add_time_zone_tables_to_query_tables(YYTHD)) - YYABORT; + MYSQL_YYABORT; else if (spc && tmp == &sys_autocommit) { /* @@ -9915,8 +10385,8 @@ internal_variable_name: LEX *lex= Lex; if (check_reserved_words(&$1)) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER && (!my_strcasecmp(system_charset_info, $1.str, "NEW") || @@ -9925,18 +10395,18 @@ internal_variable_name: if ($1.str[0]=='O' || $1.str[0]=='o') { my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "OLD", ""); - YYABORT; + MYSQL_YYABORT; } if (lex->trg_chistics.event == TRG_EVENT_DELETE) { my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE"); - YYABORT; + MYSQL_YYABORT; } if (lex->trg_chistics.action_time == TRG_ACTION_AFTER) { my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "NEW", "after "); - YYABORT; + MYSQL_YYABORT; } /* This special combination will denote field of NEW row */ $$.var= trg_new_row_fake_var; @@ -9946,7 +10416,7 @@ internal_variable_name: { sys_var *tmp=find_sys_var($3.str, $3.length); if (!tmp) - YYABORT; + MYSQL_YYABORT; if (!tmp->is_struct()) my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str); $$.var= tmp; @@ -9957,7 +10427,7 @@ internal_variable_name: { sys_var *tmp=find_sys_var($3.str, $3.length); if (!tmp) - YYABORT; + MYSQL_YYABORT; if (!tmp->is_struct()) my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str); $$.var= tmp; @@ -10009,7 +10479,7 @@ lock: if (lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "LOCK"); - YYABORT; + MYSQL_YYABORT; } lex->sql_command= SQLCOM_LOCK_TABLES; } @@ -10029,7 +10499,7 @@ table_lock: table_ident opt_table_alias lock_option { if (!Select->add_table_to_list(YYTHD, $1, $2, 0, (thr_lock_type) $3)) - YYABORT; + MYSQL_YYABORT; } ; @@ -10048,7 +10518,7 @@ unlock: if (lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "UNLOCK"); - YYABORT; + MYSQL_YYABORT; } lex->sql_command= SQLCOM_UNLOCK_TABLES; } @@ -10068,11 +10538,11 @@ handler: if (lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); - YYABORT; + MYSQL_YYABORT; } lex->sql_command = SQLCOM_HA_OPEN; if (!lex->current_select->add_table_to_list(lex->thd, $2, $4, 0)) - YYABORT; + MYSQL_YYABORT; } | HANDLER_SYM table_ident_nodb CLOSE_SYM { @@ -10080,11 +10550,11 @@ handler: if (lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); - YYABORT; + MYSQL_YYABORT; } lex->sql_command = SQLCOM_HA_CLOSE; if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0)) - YYABORT; + MYSQL_YYABORT; } | HANDLER_SYM table_ident_nodb READ_SYM { @@ -10092,7 +10562,7 @@ handler: if (lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); - YYABORT; + MYSQL_YYABORT; } lex->expr_allows_subselect= FALSE; lex->sql_command = SQLCOM_HA_READ; @@ -10100,7 +10570,7 @@ handler: lex->current_select->select_limit= new Item_int((int32) 1); lex->current_select->offset_limit= 0; if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0)) - YYABORT; + MYSQL_YYABORT; } handler_read_or_scan where_clause opt_limit_clause { @@ -10129,7 +10599,7 @@ handler_rkey_function: lex->ha_read_mode = RKEY; lex->ha_rkey_mode=$1; if (!(lex->insert_list = new List_item)) - YYABORT; + MYSQL_YYABORT; } '(' values ')' { } ; @@ -10161,8 +10631,8 @@ revoke_command: LEX *lex= Lex; if (lex->columns.elements) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } lex->sql_command= SQLCOM_REVOKE; lex->type= TYPE_ENUM_FUNCTION; @@ -10174,8 +10644,8 @@ revoke_command: LEX *lex= Lex; if (lex->columns.elements) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } lex->sql_command= SQLCOM_REVOKE; lex->type= TYPE_ENUM_PROCEDURE; @@ -10207,8 +10677,8 @@ grant_command: LEX *lex= Lex; if (lex->columns.elements) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } lex->sql_command= SQLCOM_GRANT; lex->type= TYPE_ENUM_FUNCTION; @@ -10220,8 +10690,8 @@ grant_command: LEX *lex= Lex; if (lex->columns.elements) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } lex->sql_command= SQLCOM_GRANT; lex->type= TYPE_ENUM_PROCEDURE; @@ -10300,7 +10770,7 @@ require_list_element: if (lex->x509_subject) { my_error(ER_DUP_ARGUMENT, MYF(0), "SUBJECT"); - YYABORT; + MYSQL_YYABORT; } lex->x509_subject=$2.str; } @@ -10310,7 +10780,7 @@ require_list_element: if (lex->x509_issuer) { my_error(ER_DUP_ARGUMENT, MYF(0), "ISSUER"); - YYABORT; + MYSQL_YYABORT; } lex->x509_issuer=$2.str; } @@ -10320,7 +10790,7 @@ require_list_element: if (lex->ssl_cipher) { my_error(ER_DUP_ARGUMENT, MYF(0), "CIPHER"); - YYABORT; + MYSQL_YYABORT; } lex->ssl_cipher=$2.str; } @@ -10329,17 +10799,18 @@ require_list_element: grant_ident: '*' { - LEX *lex= Lex; - THD *thd= lex->thd; - if (thd->copy_db_to(&lex->current_select->db, NULL)) - YYABORT; + THD *thd= YYTHD; + LEX *lex= thd->lex; + uint dummy; + if (thd->copy_db_to(&lex->current_select->db, &dummy)) + MYSQL_YYABORT; if (lex->grant == GLOBAL_ACLS) lex->grant = DB_ACLS & ~GRANT_ACL; else if (lex->columns.elements) { my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE), MYF(0)); - YYABORT; + MYSQL_YYABORT; } } | ident '.' '*' @@ -10352,7 +10823,7 @@ grant_ident: { my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE), MYF(0)); - YYABORT; + MYSQL_YYABORT; } } | '*' '.' '*' @@ -10365,14 +10836,14 @@ grant_ident: { my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE), MYF(0)); - YYABORT; + MYSQL_YYABORT; } } | table_ident { LEX *lex=Lex; if (!lex->current_select->add_table_to_list(lex->thd, $1,NULL,0)) - YYABORT; + MYSQL_YYABORT; if (lex->grant == GLOBAL_ACLS) lex->grant = TABLE_ACLS & ~GRANT_ACL; } @@ -10380,21 +10851,21 @@ grant_ident: user_list: - user { if (Lex->users_list.push_back($1)) YYABORT;} + user { if (Lex->users_list.push_back($1)) MYSQL_YYABORT;} | user_list ',' user { if (Lex->users_list.push_back($3)) - YYABORT; + MYSQL_YYABORT; } ; grant_list: - grant_user { if (Lex->users_list.push_back($1)) YYABORT;} + grant_user { if (Lex->users_list.push_back($1)) MYSQL_YYABORT;} | grant_list ',' grant_user { if (Lex->users_list.push_back($3)) - YYABORT; + MYSQL_YYABORT; } ; @@ -10617,17 +11088,17 @@ union_list: { /* Only the last SELECT can have INTO...... */ my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO"); - YYABORT; + MYSQL_YYABORT; } if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } /* This counter shouldn't be incremented for UNION parts */ Lex->nest_level--; if (mysql_new_select(lex, 0)) - YYABORT; + MYSQL_YYABORT; mysql_init_select(lex); lex->current_select->linkage=UNION_TYPE; if ($2) /* UNION DISTINCT - remember position */ @@ -10692,7 +11163,6 @@ subselect: } | '(' subselect_start subselect ')' { - LEX *lex= Lex; THD *thd= YYTHD; /* note that a local variable can't be used for @@ -10721,8 +11191,8 @@ subselect_start: LEX *lex=Lex; if (!lex->expr_allows_subselect) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } /* we are making a "derived table" for the parenthesis @@ -10732,7 +11202,7 @@ subselect_start: SELECT * FROM ((SELECT ...) UNION ...) */ if (mysql_new_select(Lex, 1)) - YYABORT; + MYSQL_YYABORT; }; subselect_end: @@ -10743,6 +11213,12 @@ subselect_end: lex->current_select = lex->current_select->return_after_parsing(); lex->nest_level--; lex->current_select->n_child_sum_items += child->n_sum_items; + /* + A subselect can add fields to an outer select. Reserve space for + them. + */ + lex->current_select->select_n_where_fields+= + child->select_n_where_fields; }; /************************************************************************** @@ -10846,7 +11322,7 @@ view_tail: lex->sql_command= SQLCOM_CREATE_VIEW; /* first table in list is target VIEW name */ if (!lex->select_lex.add_table_to_list(thd, $3, NULL, TL_OPTION_UPDATING)) - YYABORT; + MYSQL_YYABORT; } view_list_opt AS view_select view_check_option {} @@ -10872,9 +11348,27 @@ view_list: ; view_select: + { + LEX *lex= Lex; + lex->parsing_options.allows_variable= FALSE; + lex->parsing_options.allows_select_into= FALSE; + lex->parsing_options.allows_select_procedure= FALSE; + lex->parsing_options.allows_derived= FALSE; + } + view_select_aux + { + LEX *lex= Lex; + lex->parsing_options.allows_variable= TRUE; + lex->parsing_options.allows_select_into= TRUE; + lex->parsing_options.allows_select_procedure= TRUE; + lex->parsing_options.allows_derived= TRUE; + } + ; + +view_select_aux: SELECT_SYM remember_name select_init2 { - THD *thd=YYTHD; + THD *thd= YYTHD; LEX *lex= thd->lex; char *stmt_beg= (lex->sphead ? (char *)lex->sphead->m_tmp_query : @@ -10883,7 +11377,7 @@ view_select: } | '(' remember_name select_paren ')' union_opt { - THD *thd=YYTHD; + THD *thd= YYTHD; LEX *lex= thd->lex; char *stmt_beg= (lex->sphead ? (char *)lex->sphead->m_tmp_query : @@ -10919,11 +11413,11 @@ trigger_tail: if (lex->sphead) { my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "TRIGGER"); - YYABORT; + MYSQL_YYABORT; } if (!(sp= new sp_head())) - YYABORT; + MYSQL_YYABORT; sp->reset_thd_mem_root(YYTHD); sp->init(lex); sp->init_sp_name(YYTHD, $3); @@ -10961,7 +11455,7 @@ trigger_tail: sp->restore_thd_mem_root(YYTHD); if (sp->is_not_allowed_in_function("trigger")) - YYABORT; + MYSQL_YYABORT; /* We have to do it after parsing trigger body, because some of @@ -10972,7 +11466,7 @@ trigger_tail: (LEX_STRING*) 0, TL_OPTION_UPDATING, TL_IGNORE)) - YYABORT; + MYSQL_YYABORT; } ; @@ -11000,7 +11494,7 @@ sp_tail: if (lex->sphead) { my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE"); - YYABORT; + MYSQL_YYABORT; } lex->stmt_definition_begin= $2; @@ -11089,23 +11583,23 @@ xa: XA_SYM begin_or_start xid opt_join_or_resume xid: text_string { - YYERROR_UNLESS($1->length() <= MAXGTRIDSIZE); + MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE); if (!(Lex->xid=(XID *)YYTHD->alloc(sizeof(XID)))) - YYABORT; + MYSQL_YYABORT; Lex->xid->set(1L, $1->ptr(), $1->length(), 0, 0); } | text_string ',' text_string { - YYERROR_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE); + MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE); if (!(Lex->xid=(XID *)YYTHD->alloc(sizeof(XID)))) - YYABORT; + MYSQL_YYABORT; Lex->xid->set(1L, $1->ptr(), $1->length(), $3->ptr(), $3->length()); } | text_string ',' text_string ',' ulong_num { - YYERROR_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE); + MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE); if (!(Lex->xid=(XID *)YYTHD->alloc(sizeof(XID)))) - YYABORT; + MYSQL_YYABORT; Lex->xid->set($5, $1->ptr(), $1->length(), $3->ptr(), $3->length()); } ; diff --git a/sql/stacktrace.c b/sql/stacktrace.c index a2fe2ab88f1..d8e9b7fd883 100644 --- a/sql/stacktrace.c +++ b/sql/stacktrace.c @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -186,7 +185,7 @@ terribly wrong...\n"); #if defined(__alpha__) && defined(__GNUC__) uchar** new_fp = find_prev_fp(pc, fp); - if (frame_count == SIGRETURN_FRAME_COUNT - 1) + if (frame_count == sigreturn_frame_count - 1) { new_fp += 90; } diff --git a/sql/stacktrace.h b/sql/stacktrace.h index 527d10d70a2..f5c92e54e1c 100644 --- a/sql/stacktrace.h +++ b/sql/stacktrace.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/strfunc.cc b/sql/strfunc.cc index 2525703172f..71b52a5145d 100644 --- a/sql/strfunc.cc +++ b/sql/strfunc.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -150,7 +149,7 @@ uint find_type2(TYPELIB *typelib, const char *x, uint length, CHARSET_INFO *cs) int pos; const char *j; DBUG_ENTER("find_type2"); - DBUG_PRINT("enter",("x: '%.*s' lib: 0x%lx", length, x, typelib)); + DBUG_PRINT("enter",("x: '%.*s' lib: 0x%lx", length, x, (long) typelib)); if (!typelib->count) { @@ -312,3 +311,33 @@ outp: return (uint32) (to - to_start); } + + +/* + Searches for a LEX_STRING in an LEX_STRING array. + + SYNOPSIS + find_string_in_array() + heap The array + needle The string to search for + + NOTE + The last LEX_STRING in the array should have str member set to NULL + + RETURN VALUES + -1 Not found + >=0 Ordinal position +*/ + +int find_string_in_array(LEX_STRING * const haystack, LEX_STRING * const needle, + CHARSET_INFO * const cs) +{ + const LEX_STRING *pos; + for (pos= haystack; pos->str; pos++) + if (!cs->coll->strnncollsp(cs, (uchar *) pos->str, pos->length, + (uchar *) needle->str, needle->length, 0)) + { + return (pos - haystack); + } + return -1; +} diff --git a/sql/structs.h b/sql/structs.h index 83ae6cac032..377d337dcfa 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/table.cc b/sql/table.cc index 8505b15459b..560f53bae26 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -30,7 +29,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, File file); static void fix_type_pointers(const char ***array, TYPELIB *point_to_type, uint types, char **names); -static uint find_field(Field **fields, uint start, uint length); +static uint find_field(Field **fields, byte *record, uint start, uint length); /* Get column name from column hash */ @@ -121,9 +120,7 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key, share->normalized_path.length= path_length; share->version= refresh_version; - share->flush_version= flush_version; -#ifdef HAVE_ROW_BASED_REPLICATION /* This constant is used to mark that no table map version has been assigned. No arithmetic is done on the value: it will be @@ -141,8 +138,6 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key, share->table_map_id= ~0UL; share->cached_row_logging_check= -1; -#endif - memcpy((char*) &share->mem_root, (char*) &mem_root, sizeof(mem_root)); pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST); pthread_cond_init(&share->cond, NULL); @@ -194,7 +189,6 @@ void init_tmp_table_share(TABLE_SHARE *share, const char *key, share->path.length= share->normalized_path.length= strlen(path); share->frm_version= FRM_VER_TRUE_VARCHAR; -#ifdef HAVE_ROW_BASED_REPLICATION /* Temporary tables are not replicated, but we set up these fields anyway to be able to catch errors. @@ -202,7 +196,6 @@ void init_tmp_table_share(TABLE_SHARE *share, const char *key, share->table_map_version= ~(ulonglong)0; share->table_map_id= ~0UL; share->cached_row_logging_check= -1; -#endif DBUG_VOID_RETURN; } @@ -363,25 +356,24 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags) error= open_binary_frm(thd, share, head, file); *root_ptr= old_root; - if (share->db.length == 5 && - !my_strcasecmp(system_charset_info, share->db.str, "mysql")) + if (share->db.length == 5 && !(lower_case_table_names ? + my_strcasecmp(system_charset_info, share->db.str, "mysql") : + strcmp(share->db.str, "mysql"))) { /* We can't mark all tables in 'mysql' database as system since we don't allow to lock such tables for writing with any other tables (even with other system tables) and some privilege tables need this. */ - if (!my_strcasecmp(system_charset_info, share->table_name.str, "proc")) + if (!(lower_case_table_names ? + my_strcasecmp(system_charset_info, share->table_name.str, "proc") : + strcmp(share->table_name.str, "proc"))) share->system_table= 1; else { - if (!my_strcasecmp(system_charset_info, share->table_name.str, - "general_log")) - share->log_table= QUERY_LOG_GENERAL; - else - if (!my_strcasecmp(system_charset_info, share->table_name.str, - "slow_log")) - share->log_table= QUERY_LOG_SLOW; + share->log_table= check_if_log_table(share->db.length, share->db.str, + share->table_name.length, + share->table_name.str, 0); } } error_given= 1; @@ -480,7 +472,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, sql_print_warning("'%s' had no or invalid character set, " "and default character set is multi-byte, " "so character column sizes may have changed", - share->path); + share->path.str); } share->table_charset= default_charset_info; } @@ -787,6 +779,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, memcpy(comment_pos, disk_buff+read_length-com_length, com_length); fix_type_pointers(&interval_array, &share->fieldnames, 1, &names); + if (share->fieldnames.count != share->fields) + goto err; fix_type_pointers(&interval_array, share->intervals, interval_count, &names); @@ -805,17 +799,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, { char *val= (char*) interval->type_names[count]; interval->type_lengths[count]= strlen(val); - /* - Replace all ',' symbols with NAMES_SEP_CHAR. - See the comment in unireg.cc, pack_fields() function - for details. - */ - for (uint cnt= 0 ; cnt < interval->type_lengths[count] ; cnt++) - { - char c= val[cnt]; - if (c == ',') - val[cnt]= NAMES_SEP_CHAR; - } } interval->type_lengths[count]= 0; } @@ -878,7 +861,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, field_type=(enum_field_types) (uint) strpos[13]; /* charset and geometry_type share the same byte in frm */ - if (field_type == FIELD_TYPE_GEOMETRY) + if (field_type == MYSQL_TYPE_GEOMETRY) { #ifdef HAVE_SPATIAL geom_type= (Field::geometry_type) strpos[14]; @@ -953,7 +936,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, } #ifndef TO_BE_DELETED_ON_PRODUCTION - if (field_type == FIELD_TYPE_NEWDECIMAL && !share->mysql_version) + if (field_type == MYSQL_TYPE_NEWDECIMAL && !share->mysql_version) { /* Fix pack length of old decimal values from 5.0.3 -> 5.0.4 @@ -1000,7 +983,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, reg_field->field_index= i; reg_field->comment=comment; - if (field_type == FIELD_TYPE_BIT && !f_bit_as_char(pack_flag)) + if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) { if ((null_bit_pos+= field_length & 7) > 7) { @@ -1032,7 +1015,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, { uint primary_key=(uint) (find_type((char*) primary_key_name, &share->keynames, 3) - 1); - uint ha_option= handler_file->ha_table_flags(); + longlong ha_option= handler_file->ha_table_flags(); keyinfo= share->key_info; key_part= keyinfo->key_part; @@ -1070,6 +1053,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, Field *field; if (new_field_pack_flag <= 1) key_part->fieldnr= (uint16) find_field(share->field, + share->default_values, (uint) key_part->offset, (uint) key_part->length); if (!key_part->fieldnr) @@ -1078,6 +1062,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, goto err; } field= key_part->field= share->field[key_part->fieldnr-1]; + key_part->type= field->key_type(); if (field->null_ptr) { key_part->null_offset=(uint) ((byte*) field->null_ptr - @@ -1088,10 +1073,10 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, keyinfo->extra_length+= HA_KEY_NULL_LENGTH; keyinfo->key_length+= HA_KEY_NULL_LENGTH; } - if (field->type() == FIELD_TYPE_BLOB || + if (field->type() == MYSQL_TYPE_BLOB || field->real_type() == MYSQL_TYPE_VARCHAR) { - if (field->type() == FIELD_TYPE_BLOB) + if (field->type() == MYSQL_TYPE_BLOB) key_part->key_part_flag|= HA_BLOB_PART; else key_part->key_part_flag|= HA_VAR_LENGTH_PART; @@ -1143,7 +1128,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (field->key_length() != key_part->length) { #ifndef TO_BE_DELETED_ON_PRODUCTION - if (field->type() == FIELD_TYPE_NEWDECIMAL) + if (field->type() == MYSQL_TYPE_NEWDECIMAL) { /* Fix a fatal error in decimal key handling that causes crashes @@ -1182,7 +1167,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, as we need to test for NULL = NULL. */ if (field->real_maybe_null()) - key_part->key_part_flag|= HA_PART_KEY_SEG; + key_part->key_part_flag|= HA_NULL_PART; } keyinfo->usable_key_parts= usable_parts; // Filesort @@ -1233,40 +1218,35 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (share->found_next_number_field) { - /* - We must have a table object for find_ref_key to calculate field offset - */ - TABLE tmp_table; - tmp_table.record[0]= share->default_values; - reg_field= *share->found_next_number_field; - reg_field->table= &tmp_table; if ((int) (share->next_number_index= (uint) - find_ref_key(share->key_info, share->keys, reg_field, + find_ref_key(share->key_info, share->keys, + share->default_values, reg_field, &share->next_number_key_offset)) < 0) { + /* Wrong field definition */ + DBUG_ASSERT(0); reg_field->unireg_check= Field::NONE; /* purecov: inspected */ share->found_next_number_field= 0; } else reg_field->flags |= AUTO_INCREMENT_FLAG; - reg_field->table= 0; } if (share->blob_fields) { Field **ptr; - uint i, *save; + uint k, *save; /* Store offsets to blob fields to find them fast */ if (!(share->blob_field= save= (uint*) alloc_root(&share->mem_root, (uint) (share->blob_fields* sizeof(uint))))) goto err; - for (i=0, ptr= share->field ; *ptr ; ptr++, i++) + for (k=0, ptr= share->field ; *ptr ; ptr++, k++) { if ((*ptr)->flags & BLOB_FLAG) - (*save++)= i; + (*save++)= k; } } @@ -1282,7 +1262,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, share->column_bitmap_size= bitmap_buffer_size(share->fields); if (!(bitmaps= (my_bitmap_map*) alloc_root(&share->mem_root, - share->column_bitmap_size))) + share->column_bitmap_size))) goto err; bitmap_init(&share->all_set, bitmaps, share->fields, FALSE); bitmap_set_all(&share->all_set); @@ -1344,7 +1324,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, Field **field_ptr; DBUG_ENTER("open_table_from_share"); DBUG_PRINT("enter",("name: '%s.%s' form: 0x%lx", share->db.str, - share->table_name.str, outparam)); + share->table_name.str, (long) outparam)); error= 1; bzero((char*) outparam, sizeof(*outparam)); @@ -1401,6 +1381,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, if (records > 1) { memcpy(outparam->record[0], share->default_values, share->rec_buff_length); + memcpy(outparam->record[1], share->default_values, share->null_bytes); if (records > 2) memcpy(outparam->record[1], share->default_values, share->rec_buff_length); @@ -1567,21 +1548,32 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, outparam->file->auto_repair() && !(ha_open_flags & HA_OPEN_FOR_REPAIR)); - if (ha_err == HA_ERR_NO_SUCH_TABLE) - { - /* - The table did not exists in storage engine, use same error message - as if the .frm file didn't exist - */ - error= 1; - my_errno= ENOENT; - } - else + switch (ha_err) { - outparam->file->print_error(ha_err, MYF(0)); - error_reported= TRUE; - if (ha_err == HA_ERR_TABLE_DEF_CHANGED) - error= 7; + case HA_ERR_NO_SUCH_TABLE: + /* + The table did not exists in storage engine, use same error message + as if the .frm file didn't exist + */ + error= 1; + my_errno= ENOENT; + break; + case EMFILE: + /* + Too many files opened, use same error message as if the .frm + file can't open + */ + DBUG_PRINT("error", ("open file: %s failed, too many files opened (errno: %d)", + share->normalized_path.str, ha_err)); + error= 1; + my_errno= EMFILE; + break; + default: + outparam->file->print_error(ha_err, MYF(0)); + error_reported= TRUE; + if (ha_err == HA_ERR_TABLE_DEF_CHANGED) + error= 7; + break; } goto err; /* purecov: inspected */ } @@ -1970,7 +1962,7 @@ TYPELIB *typelib(MEM_ROOT *mem_root, List<String> &strings) # field number +1 */ -static uint find_field(Field **fields, uint start, uint length) +static uint find_field(Field **fields, byte *record, uint start, uint length) { Field **field; uint i, pos; @@ -1978,7 +1970,7 @@ static uint find_field(Field **fields, uint start, uint length) pos= 0; for (field= fields, i=1 ; *field ; i++,field++) { - if ((*field)->offset() == start) + if ((*field)->offset(record) == start) { if ((*field)->key_length() == length) return (i); @@ -2082,7 +2074,6 @@ File create_frm(THD *thd, const char *name, const char *db, HA_CREATE_INFO *create_info, uint keys) { register File file; - uint key_length; ulong length; char fill[IO_SIZE]; int create_flags= O_RDWR | O_TRUNC; @@ -2259,7 +2250,7 @@ char *get_field(MEM_ROOT *mem, Field *field) SYNPOSIS check_db_name() - name Name of database + org_name Name of database and length NOTES If lower_case_table_names is set then database is converted to lower case @@ -2269,35 +2260,35 @@ char *get_field(MEM_ROOT *mem, Field *field) 1 error */ -bool check_db_name(char *name) +bool check_db_name(LEX_STRING *org_name) { - char *start= name; - /* Used to catch empty names and names with end space */ - bool last_char_is_space= TRUE; + char *name= org_name->str; + + if (!org_name->length || org_name->length > NAME_LEN) + return 1; if (lower_case_table_names && name != any_db) my_casedn_str(files_charset_info, name); - while (*name) - { #if defined(USE_MB) && defined(USE_MB_IDENT) - last_char_is_space= my_isspace(system_charset_info, *name); - if (use_mb(system_charset_info)) + if (use_mb(system_charset_info)) + { + bool last_char_is_space= TRUE; + char *end= name + org_name->length; + while (name < end) { - int len=my_ismbchar(system_charset_info, name, - name+system_charset_info->mbmaxlen); - if (len) - { - name += len; - continue; - } + int len; + last_char_is_space= my_isspace(system_charset_info, *name); + len= my_ismbchar(system_charset_info, name, end); + if (!len) + len= 1; + name+= len; } -#else - last_char_is_space= *name==' '; -#endif - name++; + return last_char_is_space; } - return last_char_is_space || (uint) (name - start) > NAME_LEN; + else +#endif + return org_name->str[org_name->length - 1] != ' '; /* purecov: inspected */ } @@ -2406,8 +2397,8 @@ table_check_intact(TABLE *table, const uint table_f_count, my_bool error= FALSE; my_bool fields_diff_count; DBUG_ENTER("table_check_intact"); - DBUG_PRINT("info",("table=%s expected_count=%d",table->alias, table_f_count)); - DBUG_PRINT("info",("last_create_time=%d", *last_create_time)); + DBUG_PRINT("info",("table: %s expected_count: %d last_create_time: %ld", + table->alias, table_f_count, *last_create_time)); if ((fields_diff_count= (table->s->fields != table_f_count)) || (*last_create_time != table->file->stats.create_time)) @@ -2899,7 +2890,9 @@ void st_table_list::hide_view_error(THD *thd) thd->net.last_errno == ER_SP_DOES_NOT_EXIST || thd->net.last_errno == ER_PROCACCESS_DENIED_ERROR || thd->net.last_errno == ER_COLUMNACCESS_DENIED_ERROR || - thd->net.last_errno == ER_TABLEACCESS_DENIED_ERROR) + thd->net.last_errno == ER_TABLEACCESS_DENIED_ERROR || + thd->net.last_errno == ER_TABLE_NOT_LOCKED || + thd->net.last_errno == ER_NO_SUCH_TABLE) { TABLE_LIST *top= top_table(); thd->clear_error(); @@ -2980,19 +2973,17 @@ int st_table_list::view_check_option(THD *thd, bool ignore_failure) { if (check_option && check_option->val_int() == 0) { - TABLE_LIST *view= top_table(); + TABLE_LIST *main_view= top_table(); if (ignore_failure) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_VIEW_CHECK_FAILED, ER(ER_VIEW_CHECK_FAILED), - view->view_db.str, view->view_name.str); + main_view->view_db.str, main_view->view_name.str); return(VIEW_CHECK_SKIP); } - else - { - my_error(ER_VIEW_CHECK_FAILED, MYF(0), view->view_db.str, view->view_name.str); - return(VIEW_CHECK_ERROR); - } + my_error(ER_VIEW_CHECK_FAILED, MYF(0), main_view->view_db.str, + main_view->view_name.str); + return(VIEW_CHECK_ERROR); } return(VIEW_CHECK_OK); } @@ -3004,19 +2995,20 @@ int st_table_list::view_check_option(THD *thd, bool ignore_failure) SYNOPSIS st_table_list::check_single_table() - table reference on variable where to store found table + table_arg reference on variable where to store found table (should be 0 on call, to find table, or point to table for unique test) map bit mask of tables - view view for which we are looking table + view_arg view for which we are looking table RETURN FALSE table not found or found only one TRUE found several tables */ -bool st_table_list::check_single_table(st_table_list **table, table_map map, - st_table_list *view) +bool st_table_list::check_single_table(st_table_list **table_arg, + table_map map, + st_table_list *view_arg) { for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) { @@ -3024,13 +3016,13 @@ bool st_table_list::check_single_table(st_table_list **table, table_map map, { if (tbl->table->map & map) { - if (*table) + if (*table_arg) return TRUE; - *table= tbl; - tbl->check_option= view->check_option; + *table_arg= tbl; + tbl->check_option= view_arg->check_option; } } - else if (tbl->check_single_table(table, map, view)) + else if (tbl->check_single_table(table_arg, map, view_arg)) return TRUE; } return FALSE; @@ -3268,7 +3260,18 @@ bool st_table_list::prepare_view_securety_context(THD *thd) } else { - my_error(ER_NO_SUCH_USER, MYF(0), definer.user.str, definer.host.str); + if (thd->security_ctx->master_access & SUPER_ACL) + { + my_error(ER_NO_SUCH_USER, MYF(0), definer.user.str, definer.host.str); + + } + else + { + my_error(ER_ACCESS_DENIED_ERROR, MYF(0), + thd->security_ctx->priv_user, + thd->security_ctx->priv_host, + (thd->password ? ER(ER_YES) : ER(ER_NO))); + } DBUG_RETURN(TRUE); } } @@ -3344,18 +3347,19 @@ bool st_table_list::prepare_security(THD *thd) while ((tbl= tb++)) { DBUG_ASSERT(tbl->referencing_view); - char *db, *table_name; + char *local_db, *local_table_name; if (tbl->view) { - db= tbl->view_db.str; - table_name= tbl->view_name.str; + local_db= tbl->view_db.str; + local_table_name= tbl->view_name.str; } else { - db= tbl->db; - table_name= tbl->table_name; + local_db= tbl->db; + local_table_name= tbl->table_name; } - fill_effective_table_privileges(thd, &tbl->grant, db, table_name); + fill_effective_table_privileges(thd, &tbl->grant, local_db, + local_table_name); if (tbl->table) tbl->table->grant= grant; } @@ -3427,6 +3431,7 @@ Field *Natural_join_column::field() const char *Natural_join_column::table_name() { + DBUG_ASSERT(table_ref); return table_ref->alias; } @@ -3714,13 +3719,13 @@ Field_iterator_table_ref::get_or_create_column_ref(TABLE_LIST *parent_table_ref) uint field_count; TABLE_LIST *add_table_ref= parent_table_ref ? parent_table_ref : table_ref; - LINT_INIT(field_count); + if (field_it == &table_field_it) { /* The field belongs to a stored table. */ - Field *field= table_field_it.field(); - nj_col= new Natural_join_column(field, table_ref); + Field *tmp_field= table_field_it.field(); + nj_col= new Natural_join_column(tmp_field, table_ref); field_count= table_ref->table->s->fields; } else if (field_it == &view_field_it) @@ -4082,21 +4087,38 @@ void st_table_list::reinit_before_use(THD *thd) */ table= 0; /* Reset is_schema_table_processed value(needed for I_S tables */ - is_schema_table_processed= FALSE; + schema_table_state= NOT_PROCESSED; TABLE_LIST *embedded; /* The table at the current level of nesting. */ - TABLE_LIST *embedding= this; /* The parent nested table reference. */ + TABLE_LIST *parent_embedding= this; /* The parent nested table reference. */ do { - embedded= embedding; + embedded= parent_embedding; if (embedded->prep_on_expr) embedded->on_expr= embedded->prep_on_expr->copy_andor_structure(thd); - embedding= embedded->embedding; + parent_embedding= embedded->embedding; } - while (embedding && - embedding->nested_join->join_list.head() == embedded); + while (parent_embedding && + parent_embedding->nested_join->join_list.head() == embedded); } +/* + Return subselect that contains the FROM list this table is taken from + + SYNOPSIS + st_table_list::containing_subselect() + + RETURN + Subselect item for the subquery that contains the FROM list + this table is taken from if there is any + 0 - otherwise + +*/ + +Item_subselect *st_table_list::containing_subselect() +{ + return (select_lex ? select_lex->master_unit()->item : 0); +} /***************************************************************************** ** Instansiate templates diff --git a/sql/table.h b/sql/table.h index 434fb5a4d11..54c820d391c 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -18,6 +17,7 @@ /* Structs that defines the TABLE */ class Item; /* Needed by ORDER */ +class Item_subselect; class GRANT_TABLE; class st_select_lex_unit; class st_select_lex; @@ -74,6 +74,9 @@ enum release_type { RELEASE_NORMAL, RELEASE_WAIT_FOR_DROP }; typedef struct st_filesort_info { IO_CACHE *io_cache; /* If sorted through filebyte */ + uchar **sort_keys; /* Buffer for sorting keys */ + byte *buffpek; /* Buffer for buffpek structures */ + uint buffpek_len; /* Max number of buffpeks in the buffer */ byte *addon_buf; /* Pointer to a buffer if sorted with fields */ uint addon_length; /* Length of the buffer */ struct st_sort_addon_field *addon_field; /* Pointer to the fields info */ @@ -112,6 +115,7 @@ class Table_triggers_list; typedef struct st_table_share { + st_table_share() {} /* Remove gcc warning */ /* hash of field names (contains pointers to elements of field array) */ HASH name_hash; /* hash of field names */ MEM_ROOT mem_root; @@ -154,12 +158,17 @@ typedef struct st_table_share LEX_STRING path; /* Path to .frm file (from datadir) */ LEX_STRING normalized_path; /* unpack_filename(path) */ LEX_STRING connect_string; - key_map keys_in_use; /* Keys in use for table */ + + /* + Set of keys in use, implemented as a Bitmap. + Excludes keys disabled by ALTER TABLE ... DISABLE KEYS. + */ + key_map keys_in_use; key_map keys_for_keyread; ha_rows min_rows, max_rows; /* create information */ ulong avg_row_length; /* create information */ ulong raid_chunksize; - ulong version, flush_version, mysql_version; + ulong version, mysql_version; ulong timestamp_offset; /* Set to offset+1 of record */ ulong reclength; /* Recordlength */ @@ -309,7 +318,21 @@ struct st_table { byte *write_row_record; /* Used as optimisation in THD::write_row */ byte *insert_values; /* used by INSERT ... UPDATE */ - key_map quick_keys, used_keys, keys_in_use_for_query, merge_keys; + key_map quick_keys, used_keys; + + /* + A set of keys that can be used in the query that references this + table. + + All indexes disabled on the table's TABLE_SHARE (see TABLE::s) will be + subtracted from this set upon instantiation. Thus for any TABLE t it holds + that t.keys_in_use_for_query is a subset of t.s.keys_in_use. Generally we + must not introduce any new keys here (see setup_tables). + + The set is implemented as a bitmap. + */ + key_map keys_in_use_for_query; + key_map merge_keys; KEY *key_info; /* data of keys in database */ Field *next_number_field; /* Set if next_number is activated */ @@ -385,6 +408,10 @@ struct st_table { /* If true, the current table row is considered to have all columns set to NULL, including columns declared as "not null" (see maybe_null). + + TODO: Each of these flags take up 8 bits. They can just as easily + be put into one single unsigned long and instead of taking up 18 + bytes, it would take up 4. */ my_bool null_row; my_bool force_index; @@ -392,6 +419,7 @@ struct st_table { my_bool key_read, no_keyread; my_bool locked_by_flush; my_bool locked_by_logger; + my_bool no_replicate; my_bool locked_by_name; my_bool fulltext_searched; my_bool no_cache; @@ -448,6 +476,12 @@ struct st_table { }; +enum enum_schema_table_state +{ + NOT_PROCESSED= 0, + PROCESSED_BY_CREATE_SORT_INDEX, + PROCESSED_BY_JOIN_EXEC +}; typedef struct st_foreign_key_info { @@ -456,6 +490,7 @@ typedef struct st_foreign_key_info LEX_STRING *referenced_table; LEX_STRING *update_method; LEX_STRING *delete_method; + LEX_STRING *referenced_key_name; List<LEX_STRING> foreign_fields; List<LEX_STRING> referenced_fields; } FOREIGN_KEY_INFO; @@ -706,7 +741,6 @@ typedef struct st_table_list st_select_lex_unit *derived; /* SELECT_LEX_UNIT of derived table */ ST_SCHEMA_TABLE *schema_table; /* Information_schema table */ st_select_lex *schema_select_lex; - bool is_schema_table_processed; /* True when the view field translation table is used to convert schema table fields for backwards compatibility with SHOW command. @@ -816,12 +850,13 @@ typedef struct st_table_list */ bool prelocking_placeholder; + enum enum_schema_table_state schema_table_state; void calc_md5(char *buffer); void set_underlying_merge(); int view_check_option(THD *thd, bool ignore_failure); bool setup_underlying(THD *thd); void cleanup_items(); - bool placeholder() {return derived || view; } + bool placeholder() {return derived || view || schema_table || !table; } void print(THD *thd, String *str); bool check_single_table(st_table_list **table, table_map map, st_table_list *view); @@ -859,6 +894,7 @@ typedef struct st_table_list procedure. */ void reinit_before_use(THD *thd); + Item_subselect *containing_subselect(); private: bool prep_check_option(THD *thd, uint8 check_opt_type); diff --git a/sql/thr_malloc.cc b/sql/thr_malloc.cc index 3a9ca397bba..392db9224c3 100644 --- a/sql/thr_malloc.cc +++ b/sql/thr_malloc.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2001, 2003-2004 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/time.cc b/sql/time.cc index 0461f7723c6..4854206b1c8 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -25,14 +24,25 @@ #ifndef TESTTIME +/* + Name description of interval names used in statements. + + 'interval_type_to_name' is ordered and sorted on interval size and + interval complexity. + Order of elements in 'interval_type_to_name' should correspond to + the order of elements in 'interval_type' enum + + See also interval_type, interval_names +*/ + LEX_STRING interval_type_to_name[INTERVAL_LAST] = { { C_STRING_WITH_LEN("YEAR")}, { C_STRING_WITH_LEN("QUARTER")}, { C_STRING_WITH_LEN("MONTH")}, + { C_STRING_WITH_LEN("WEEK")}, { C_STRING_WITH_LEN("DAY")}, { C_STRING_WITH_LEN("HOUR")}, { C_STRING_WITH_LEN("MINUTE")}, - { C_STRING_WITH_LEN("WEEK")}, { C_STRING_WITH_LEN("SECOND")}, { C_STRING_WITH_LEN("MICROSECOND")}, { C_STRING_WITH_LEN("YEAR_MONTH")}, @@ -253,14 +263,11 @@ my_time_t TIME_to_timestamp(THD *thd, const TIME *t, my_bool *in_dst_time_gap) *in_dst_time_gap= 0; - if (t->year < TIMESTAMP_MAX_YEAR && t->year > TIMESTAMP_MIN_YEAR || - t->year == TIMESTAMP_MAX_YEAR && t->month == 1 && t->day == 1 || - t->year == TIMESTAMP_MIN_YEAR && t->month == 12 && t->day == 31) + timestamp= thd->variables.time_zone->TIME_to_gmt_sec(t, in_dst_time_gap); + if (timestamp) { thd->time_zone_used= 1; - timestamp= thd->variables.time_zone->TIME_to_gmt_sec(t, in_dst_time_gap); - if (timestamp >= TIMESTAMP_MIN_VALUE && timestamp <= TIMESTAMP_MAX_VALUE) - return timestamp; + return timestamp; } /* If we are here we have range error. */ @@ -278,9 +285,9 @@ my_time_t TIME_to_timestamp(THD *thd, const TIME *t, my_bool *in_dst_time_gap) bool str_to_time_with_warn(const char *str, uint length, TIME *l_time) { - int was_cut; - bool ret_val= str_to_time(str, length, l_time, &was_cut); - if (was_cut) + int warning; + bool ret_val= str_to_time(str, length, l_time, &warning); + if (ret_val || warning) make_truncated_value_warning(current_thd, str, length, MYSQL_TIMESTAMP_TIME, NullS); return ret_val; diff --git a/sql/tzfile.h b/sql/tzfile.h index 623cddc1f12..1a57c0c5f69 100644 --- a/sql/tzfile.h +++ b/sql/tzfile.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/tztime.cc b/sql/tztime.cc index a1bcf25bb51..914c7be46de 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -547,8 +546,8 @@ sec_to_TIME(TIME * tmp, my_time_t t, long offset) int yleap; const uint *ip; - days= t / SECS_PER_DAY; - rem= t % SECS_PER_DAY; + days= (long) (t / SECS_PER_DAY); + rem= (long) (t % SECS_PER_DAY); /* We do this as separate step after dividing t, because this @@ -781,6 +780,8 @@ gmt_sec_to_TIME(TIME *tmp, my_time_t sec_in_utc, const TIME_ZONE_INFO *sp) static my_time_t sec_since_epoch(int year, int mon, int mday, int hour, int min ,int sec) { + /* Guard against my_time_t overflow(on system with 32 bit my_time_t) */ + DBUG_ASSERT(!(year == TIMESTAMP_MAX_YEAR && mon == 1 && mday > 17)); #ifndef WE_WANT_TO_HANDLE_UNORMALIZED_DATES /* It turns out that only whenever month is normalized or unnormalized @@ -898,8 +899,14 @@ TIME_to_gmt_sec(const TIME *t, const TIME_ZONE_INFO *sp, my_time_t local_t; uint saved_seconds; uint i; + int shift= 0; + DBUG_ENTER("TIME_to_gmt_sec"); + if (!validate_timestamp_range(t)) + DBUG_RETURN(0); + + /* We need this for correct leap seconds handling */ if (t->second < SECS_PER_MIN) saved_seconds= 0; @@ -907,11 +914,29 @@ TIME_to_gmt_sec(const TIME *t, const TIME_ZONE_INFO *sp, saved_seconds= t->second; /* - NOTE If we want to convert full my_time_t range without MySQL - restrictions we should catch overflow here somehow. + NOTE: to convert full my_time_t range we do a shift of the + boundary dates here to avoid overflow of my_time_t. + We use alike approach in my_system_gmt_sec(). + + However in that function we also have to take into account + overflow near 0 on some platforms. That's because my_system_gmt_sec + uses localtime_r(), which doesn't work with negative values correctly + on platforms with unsigned time_t (QNX). Here we don't use localtime() + => we negative values of local_t are ok. */ - local_t= sec_since_epoch(t->year, t->month, t->day, + if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4) + { + /* + We will pass (t->day - shift) to sec_since_epoch(), and + want this value to be a positive number, so we shift + only dates > 4.01.2038 (to avoid owerflow). + */ + shift= 2; + } + + + local_t= sec_since_epoch(t->year, t->month, (t->day - shift), t->hour, t->minute, saved_seconds ? 0 : t->second); @@ -930,6 +955,21 @@ TIME_to_gmt_sec(const TIME *t, const TIME_ZONE_INFO *sp, /* binary search for our range */ i= find_time_range(local_t, sp->revts, sp->revcnt); + /* + As there are no offset switches at the end of TIMESTAMP range, + we could simply check for overflow here (and don't need to bother + about DST gaps etc) + */ + if (shift) + { + if (local_t > (my_time_t) (TIMESTAMP_MAX_VALUE - shift * SECS_PER_DAY + + sp->revtis[i].rt_offset - saved_seconds)) + { + DBUG_RETURN(0); /* my_time_t overflow */ + } + local_t+= shift * SECS_PER_DAY; + } + if (sp->revtis[i].rt_type) { /* @@ -939,10 +979,16 @@ TIME_to_gmt_sec(const TIME *t, const TIME_ZONE_INFO *sp, beginning of the gap. */ *in_dst_time_gap= 1; - DBUG_RETURN(sp->revts[i] - sp->revtis[i].rt_offset + saved_seconds); + local_t= sp->revts[i] - sp->revtis[i].rt_offset + saved_seconds; } else - DBUG_RETURN(local_t - sp->revtis[i].rt_offset + saved_seconds); + local_t= local_t - sp->revtis[i].rt_offset + saved_seconds; + + /* check for TIMESTAMP_MAX_VALUE was already done above */ + if (local_t < TIMESTAMP_MIN_VALUE) + local_t= 0; + + DBUG_RETURN(local_t); } @@ -1308,9 +1354,39 @@ Time_zone_offset::Time_zone_offset(long tz_offset_arg): my_time_t Time_zone_offset::TIME_to_gmt_sec(const TIME *t, my_bool *in_dst_time_gap) const { - return sec_since_epoch(t->year, t->month, t->day, - t->hour, t->minute, t->second) - - offset; + my_time_t local_t; + int shift= 0; + + /* + Check timestamp range.we have to do this as calling function relies on + us to make all validation checks here. + */ + if (!validate_timestamp_range(t)) + return 0; + + /* + Do a temporary shift of the boundary dates to avoid + overflow of my_time_t if the time value is near it's + maximum range + */ + if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4) + shift= 2; + + local_t= sec_since_epoch(t->year, t->month, (t->day - shift), + t->hour, t->minute, t->second) - + offset; + + if (shift) + { + /* Add back the shifted time */ + local_t+= shift * SECS_PER_DAY; + } + + if (local_t >= TIMESTAMP_MIN_VALUE && local_t <= TIMESTAMP_MAX_VALUE) + return local_t; + + /* range error*/ + return 0; } @@ -1683,9 +1759,9 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) tz_leapcnt++; DBUG_PRINT("info", - ("time_zone_leap_second table: tz_leapcnt=%u tt_time=%lld offset=%ld", - tz_leapcnt, (longlong)tz_lsis[tz_leapcnt-1].ls_trans, - tz_lsis[tz_leapcnt-1].ls_corr)); + ("time_zone_leap_second table: tz_leapcnt: %u tt_time: %lu offset: %ld", + tz_leapcnt, (ulong) tz_lsis[tz_leapcnt-1].ls_trans, + tz_lsis[tz_leapcnt-1].ls_corr)); res= table->file->index_next(table->record[0]); } @@ -1710,8 +1786,8 @@ end_with_setting_default_tz: /* If we have default time zone try to load it */ if (default_tzname) { - String tmp_tzname(default_tzname, &my_charset_latin1); - if (!(global_system_variables.time_zone= my_tz_find(&tmp_tzname, tables))) + String tmp_tzname2(default_tzname, &my_charset_latin1); + if (!(global_system_variables.time_zone= my_tz_find(&tmp_tzname2, tables))) { sql_print_error("Fatal error: Illegal or unknown default time zone '%s'", default_tzname); @@ -1997,8 +2073,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) tz_info->timecnt++; DBUG_PRINT("info", - ("time_zone_transition table: tz_id=%u tt_time=%lld tt_id=%u", - tzid, (longlong)ttime, ttid)); + ("time_zone_transition table: tz_id: %u tt_time: %lu tt_id: %u", + tzid, (ulong) ttime, ttid)); res= table->file->index_next_same(table->record[0], (byte*)table->field[0]->ptr, 4); diff --git a/sql/tztime.h b/sql/tztime.h index 95184c9b3d1..248a638074b 100644 --- a/sql/tztime.h +++ b/sql/tztime.h @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/sql/udf_example.c b/sql/udf_example.c index 2fa7474eb16..d37c6505ced 100644 --- a/sql/udf_example.c +++ b/sql/udf_example.c @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -165,6 +164,9 @@ void avgcost_reset( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error void avgcost_clear( UDF_INIT* initid, char* is_null, char *error ); void avgcost_add( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ); double avgcost( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ); +my_bool is_const_init(UDF_INIT *initid, UDF_ARGS *args, char *message); +char *is_const(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long + *length, char *is_null, char *error); /************************************************************************* @@ -682,7 +684,7 @@ longlong sequence(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, ****************************************************************************/ #ifdef __WIN__ -#include <winsock.h> +#include <winsock2.h> #else #include <sys/socket.h> #include <netinet/in.h> @@ -1075,4 +1077,32 @@ char *myfunc_argument_name(UDF_INIT *initid __attribute__((unused)), return result; } + + +my_bool is_const_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + if (args->arg_count != 1) + { + strmov(message, "IS_CONST accepts only one argument"); + return 1; + } + initid->ptr= (char*)((args->args[0] != NULL) ? 1UL : 0); + return 0; +} + +char * is_const(UDF_INIT *initid, UDF_ARGS *args __attribute__((unused)), + char *result, unsigned long *length, + char *is_null, char *error __attribute__((unused))) +{ + if (initid->ptr != 0) { + sprintf(result, "const"); + } else { + sprintf(result, "not const"); + } + *is_null= 0; + *length= strlen(result); + return result; +} + + #endif /* HAVE_DLOPEN */ diff --git a/sql/udf_example.def b/sql/udf_example.def index d3081ca7768..7a87147d7b6 100644 --- a/sql/udf_example.def +++ b/sql/udf_example.def @@ -1,5 +1,4 @@ LIBRARY udf_example -DESCRIPTION 'MySQL Sample for UDF' VERSION 1.0 EXPORTS lookup @@ -22,3 +21,5 @@ EXPORTS avgcost_add avgcost_clear avgcost + is_const + is_const_init diff --git a/sql/uniques.cc b/sql/uniques.cc index ad074f8b2b0..9eb827f62a3 100644 --- a/sql/uniques.cc +++ b/sql/uniques.cc @@ -2,8 +2,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -55,18 +54,19 @@ int unique_write_to_ptrs(gptr key, element_count count, Unique *unique) } Unique::Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg, - uint size_arg, ulong max_in_memory_size_arg) + uint size_arg, ulonglong max_in_memory_size_arg) :max_in_memory_size(max_in_memory_size_arg), size(size_arg), elements(0) { my_b_clear(&file); - init_tree(&tree, max_in_memory_size / 16, 0, size, comp_func, 0, NULL, - comp_func_fixed_arg); + init_tree(&tree, (ulong) (max_in_memory_size / 16), 0, size, comp_func, 0, + NULL, comp_func_fixed_arg); /* If the following fail's the next add will also fail */ my_init_dynamic_array(&file_ptrs, sizeof(BUFFPEK), 16, 16); /* If you change the following, change it in get_max_elements function, too. */ - max_elements= max_in_memory_size / ALIGN_SIZE(sizeof(TREE_ELEMENT)+size); + max_elements= (ulong) (max_in_memory_size / + ALIGN_SIZE(sizeof(TREE_ELEMENT)+size)); VOID(open_cached_file(&file, mysql_tmpdir,TEMP_PREFIX, DISK_BUFFER_SIZE, MYF(MY_WME))); } @@ -260,15 +260,15 @@ static double get_merge_many_buffs_cost(uint *buffer, */ double Unique::get_use_cost(uint *buffer, uint nkeys, uint key_size, - ulong max_in_memory_size) + ulonglong max_in_memory_size) { ulong max_elements_in_tree; ulong last_tree_elems; int n_full_trees; /* number of trees in unique - 1 */ double result; - max_elements_in_tree= - max_in_memory_size / ALIGN_SIZE(sizeof(TREE_ELEMENT)+key_size); + max_elements_in_tree= ((ulong) max_in_memory_size / + ALIGN_SIZE(sizeof(TREE_ELEMENT)+key_size)); n_full_trees= nkeys / max_elements_in_tree; last_tree_elems= nkeys % max_elements_in_tree; @@ -386,9 +386,11 @@ C_MODE_END /* DESCRIPTION + Function is very similar to merge_buffers, but instead of writing sorted unique keys to the output file, it invokes walk_action for each key. This saves I/O if you need to pass through all unique keys only once. + SYNOPSIS merge_walk() All params are 'IN' (but see comment for begin, end): @@ -416,7 +418,7 @@ C_MODE_END <> 0 error */ -static bool merge_walk(uchar *merge_buffer, uint merge_buffer_size, +static bool merge_walk(uchar *merge_buffer, ulong merge_buffer_size, uint key_length, BUFFPEK *begin, BUFFPEK *end, tree_walk_action walk_action, void *walk_action_arg, qsort_cmp2 compare, void *compare_arg, @@ -425,14 +427,15 @@ static bool merge_walk(uchar *merge_buffer, uint merge_buffer_size, BUFFPEK_COMPARE_CONTEXT compare_context = { compare, compare_arg }; QUEUE queue; if (end <= begin || - merge_buffer_size < key_length * (end - begin + 1) || - init_queue(&queue, end - begin, offsetof(BUFFPEK, key), 0, + merge_buffer_size < (ulong) (key_length * (end - begin + 1)) || + init_queue(&queue, (uint) (end - begin), offsetof(BUFFPEK, key), 0, buffpek_compare, &compare_context)) return 1; /* we need space for one key when a piece of merge buffer is re-read */ merge_buffer_size-= key_length; uchar *save_key_buff= merge_buffer + merge_buffer_size; - uint max_key_count_per_piece= merge_buffer_size/(end-begin)/key_length; + uint max_key_count_per_piece= (uint) (merge_buffer_size/(end-begin) / + key_length); /* if piece_size is aligned reuse_freed_buffer will always hit */ uint piece_size= max_key_count_per_piece * key_length; uint bytes_read; /* to hold return value of read_to_buffer */ @@ -548,6 +551,9 @@ end: bool Unique::walk(tree_walk_action action, void *walk_action_arg) { + int res; + uchar *merge_buffer; + if (elements == 0) /* the whole tree is in memory */ return tree_walk(&tree, action, walk_action_arg, left_root_right); @@ -556,15 +562,14 @@ bool Unique::walk(tree_walk_action action, void *walk_action_arg) return 1; if (flush_io_cache(&file) || reinit_io_cache(&file, READ_CACHE, 0L, 0, 0)) return 1; - uchar *merge_buffer= (uchar *) my_malloc(max_in_memory_size, MYF(0)); - if (merge_buffer == 0) + if (!(merge_buffer= (uchar *) my_malloc((ulong) max_in_memory_size, MYF(0)))) return 1; - int res= merge_walk(merge_buffer, max_in_memory_size, size, - (BUFFPEK *) file_ptrs.buffer, - (BUFFPEK *) file_ptrs.buffer + file_ptrs.elements, - action, walk_action_arg, - tree.compare, tree.custom_arg, &file); - x_free(merge_buffer); + res= merge_walk(merge_buffer, (ulong) max_in_memory_size, size, + (BUFFPEK *) file_ptrs.buffer, + (BUFFPEK *) file_ptrs.buffer + file_ptrs.elements, + action, walk_action_arg, + tree.compare, tree.custom_arg, &file); + my_free((char*) merge_buffer, MYF(0)); return res; } @@ -615,7 +620,7 @@ bool Unique::get(TABLE *table) sort_param.sort_form=table; sort_param.rec_length= sort_param.sort_length= sort_param.ref_length= size; - sort_param.keys= max_in_memory_size / sort_param.sort_length; + sort_param.keys= (uint) (max_in_memory_size / sort_param.sort_length); sort_param.not_killable=1; if (!(sort_buffer=(uchar*) my_malloc((sort_param.keys+1) * diff --git a/sql/unireg.cc b/sql/unireg.cc index a20c59fd796..d90420313a6 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -83,7 +82,7 @@ bool mysql_create_frm(THD *thd, const char *file_name, uchar fileinfo[64],forminfo[288],*keybuff; TYPELIB formnames; uchar *screen_buff; - char buff[32]; + char buff[128]; #ifdef WITH_PARTITION_STORAGE_ENGINE partition_info *part_info= thd->work_part_info; #endif @@ -176,7 +175,6 @@ bool mysql_create_frm(THD *thd, const char *file_name, create_info->comment.length, 60); if (tmp_len < create_info->comment.length) { - char buff[128]; (void) my_snprintf(buff, sizeof(buff), "Too long comment for table '%s'", table); if ((thd->variables.sql_mode & @@ -469,16 +467,16 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo, int2store(pos+6, key->block_size); pos+=8; key_parts+=key->key_parts; - DBUG_PRINT("loop",("flags: %d key_parts: %d at 0x%lx", - key->flags,key->key_parts, - key->key_part)); + DBUG_PRINT("loop", ("flags: %d key_parts: %d at 0x%lx", + key->flags, key->key_parts, + (long) key->key_part)); for (key_part=key->key_part,key_part_end=key_part+key->key_parts ; key_part != key_part_end ; key_part++) { uint offset; - DBUG_PRINT("loop",("field: %d startpos: %lu length: %ld", + DBUG_PRINT("loop",("field: %d startpos: %lu length: %d", key_part->fieldnr, key_part->offset + data_offset, key_part->length)); int2store(pos,key_part->fieldnr+1+FIELD_NAME_USED); @@ -550,11 +548,11 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, create_field *field; while ((field=it++)) { - uint tmp_len= system_charset_info->cset->charpos(system_charset_info, field->comment.str, field->comment.str + - field->comment.length, 255); + field->comment.length, + 255); if (tmp_len < field->comment.length) { char buff[128]; @@ -584,7 +582,7 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, We mark first TIMESTAMP field with NOW() in DEFAULT or ON UPDATE as auto-update field. */ - if (field->sql_type == FIELD_TYPE_TIMESTAMP && + if (field->sql_type == MYSQL_TYPE_TIMESTAMP && MTYP_TYPENR(field->unireg_check) != Field::NONE && !time_stamp_pos) time_stamp_pos= (uint) field->offset+ (uint) data_offset + 1; @@ -623,8 +621,9 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, for (uint pos= 0; pos < field->interval->count; pos++) { char *dst; - uint length= field->save_interval->type_lengths[pos], hex_length; const char *src= field->save_interval->type_names[pos]; + uint hex_length; + length= field->save_interval->type_lengths[pos]; hex_length= length * 2; field->interval->type_lengths[pos]= hex_length; field->interval->type_names[pos]= dst= sql_alloc(hex_length + 1); @@ -743,7 +742,7 @@ static bool pack_fields(File file, List<create_field> &create_fields, int2store(buff+10,field->unireg_check); buff[12]= (uchar) field->interval_id; buff[13]= (uchar) field->sql_type; - if (field->sql_type == FIELD_TYPE_GEOMETRY) + if (field->sql_type == MYSQL_TYPE_GEOMETRY) { buff[14]= (uchar) field->geom_type; #ifndef HAVE_SPATIAL @@ -789,29 +788,48 @@ static bool pack_fields(File file, List<create_field> &create_fields, { if (field->interval_id > int_count) { - int_count=field->interval_id; - tmp.append(NAMES_SEP_CHAR); - for (const char **pos=field->interval->type_names ; *pos ; pos++) - { - char *val= (char*) *pos; - uint str_len= strlen(val); - /* - Note, hack: in old frm NAMES_SEP_CHAR is used to separate - names in the interval (ENUM/SET). To allow names to contain - NAMES_SEP_CHAR, we replace it with a comma before writing frm. - Backward conversion is done during frm file opening, - See table.cc, openfrm() function - */ - for (uint cnt= 0 ; cnt < str_len ; cnt++) + unsigned char sep= 0; + unsigned char occ[256]; + uint i; + unsigned char *val= NULL; + + bzero(occ, sizeof(occ)); + + for (i=0; (val= (unsigned char*) field->interval->type_names[i]); i++) + for (uint j = 0; j < field->interval->type_lengths[i]; j++) + occ[(unsigned int) (val[j])]= 1; + + if (!occ[(unsigned char)NAMES_SEP_CHAR]) + sep= (unsigned char) NAMES_SEP_CHAR; + else if (!occ[(unsigned int)',']) + sep= ','; + else + { + for (uint i=1; i<256; i++) + { + if(!occ[i]) + { + sep= i; + break; + } + } + + if(!sep) /* disaster, enum uses all characters, none left as separator */ { - char c= val[cnt]; - if (c == NAMES_SEP_CHAR) - val[cnt]= ','; + my_message(ER_WRONG_FIELD_TERMINATORS,ER(ER_WRONG_FIELD_TERMINATORS), + MYF(0)); + DBUG_RETURN(1); } - tmp.append(*pos); - tmp.append(NAMES_SEP_CHAR); - } - tmp.append('\0'); // End of intervall + } + + int_count= field->interval_id; + tmp.append(sep); + for (const char **pos=field->interval->type_names ; *pos ; pos++) + { + tmp.append(*pos); + tmp.append(sep); + } + tmp.append('\0'); // End of intervall } } if (my_write(file,(byte*) tmp.ptr(),tmp.length(),MYF_RW)) @@ -895,7 +913,10 @@ static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type, field->interval, field->field_name); if (!regfield) + { + error= 1; goto err; // End of memory + } /* save_in_field() will access regfield->table->in_use */ regfield->init(&table); @@ -906,13 +927,13 @@ static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type, null_count++; } - if (field->sql_type == FIELD_TYPE_BIT && !f_bit_as_char(field->pack_flag)) + if (field->sql_type == MYSQL_TYPE_BIT && !f_bit_as_char(field->pack_flag)) null_count+= field->length & 7; type= (Field::utype) MTYP_TYPENR(field->unireg_check); if (field->def && - (regfield->real_type() != FIELD_TYPE_YEAR || + (regfield->real_type() != MYSQL_TYPE_YEAR || field->def->val_int() != 0)) { if (field->def->save_in_field(regfield, 1)) @@ -923,7 +944,7 @@ static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type, goto err; } } - else if (regfield->real_type() == FIELD_TYPE_ENUM && + else if (regfield->real_type() == MYSQL_TYPE_ENUM && (field->flags & NOT_NULL_FLAG)) { regfield->set_notnull(); diff --git a/sql/unireg.h b/sql/unireg.h index af91793e8fe..d67fa372083 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -1,9 +1,8 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2006 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of |