diff options
Diffstat (limited to 'sql')
140 files changed, 19088 insertions, 12402 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 85c2013d8e7..299f4ae4285 100644..100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -15,9 +15,10 @@ INCLUDE("${PROJECT_SOURCE_DIR}/win/mysql_manifest.cmake") SET(CMAKE_CXX_FLAGS_DEBUG - "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX -DUSE_SYMDIR") + "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX -DUSE_SYMDIR /Zi") SET(CMAKE_C_FLAGS_DEBUG - "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX -DUSE_SYMDIR") + "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX -DUSE_SYMDIR /Zi") +SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /MAP /MAPINFO:EXPORTS") INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/extra/yassl/include @@ -27,9 +28,9 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ) SET_SOURCE_FILES_PROPERTIES(${CMAKE_SOURCE_DIR}/sql/message.rc - ${CMAKE_SOURCE_DIR}/sql/message.h - ${CMAKE_SOURCE_DIR}/sql/sql_yacc.h - ${CMAKE_SOURCE_DIR}/sql/sql_yacc.cc + ${CMAKE_SOURCE_DIR}/sql/message.h + ${CMAKE_SOURCE_DIR}/sql/sql_yacc.h + ${CMAKE_SOURCE_DIR}/sql/sql_yacc.cc ${CMAKE_SOURCE_DIR}/include/mysql_version.h ${CMAKE_SOURCE_DIR}/sql/sql_builtin.cc ${CMAKE_SOURCE_DIR}/sql/lex_hash.h @@ -40,7 +41,8 @@ SET_SOURCE_FILES_PROPERTIES(${CMAKE_SOURCE_DIR}/sql/message.rc ADD_DEFINITIONS(-DMYSQL_SERVER -D_CONSOLE -DHAVE_DLOPEN) -ADD_EXECUTABLE(mysqld ../sql-common/client.c derror.cc des_key_file.cc +ADD_EXECUTABLE(mysqld + ../sql-common/client.c derror.cc des_key_file.cc discover.cc ../libmysql/errmsg.c field.cc field_conv.cc filesort.cc gstream.cc ha_partition.cc @@ -72,18 +74,29 @@ ADD_EXECUTABLE(mysqld ../sql-common/client.c derror.cc des_key_file.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 sql_servers.cc - sql_connect.cc scheduler.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 - ${PROJECT_SOURCE_DIR}/include/mysqld_ername.h - ${PROJECT_SOURCE_DIR}/include/sql_state.h - ${PROJECT_SOURCE_DIR}/include/mysql_version.h - ${PROJECT_SOURCE_DIR}/sql/sql_builtin.cc - ${PROJECT_SOURCE_DIR}/sql/lex_hash.h) -TARGET_LINK_LIBRARIES(mysqld heap myisam myisammrg mysys yassl zlib dbug yassl + ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h + ${PROJECT_SOURCE_DIR}/include/mysqld_error.h + ${PROJECT_SOURCE_DIR}/include/mysqld_ername.h + ${PROJECT_SOURCE_DIR}/include/sql_state.h + ${PROJECT_SOURCE_DIR}/include/mysql_version.h + ${PROJECT_SOURCE_DIR}/sql/sql_builtin.cc + ${PROJECT_SOURCE_DIR}/sql/lex_hash.h) + +TARGET_LINK_LIBRARIES(mysqld + heap myisam myisammrg mysys yassl zlib debug dbug yassl taocrypt strings vio regex wsock32 ws2_32) +SET_TARGET_PROPERTIES(mysqld PROPERTIES OUTPUT_NAME mysqld${MYSQLD_EXE_SUFFIX}) + +# Work around for 2.4.6 bug, OUTPUT_NAME will not set the right .PDB +# file name. Note that COMPILE_FLAGS set some temporary pdb during build, +# LINK_FLAGS sets the real one. +SET_TARGET_PROPERTIES(mysqld PROPERTIES + COMPILE_FLAGS "/Fd${CMAKE_CFG_INTDIR}/mysqld${MYSQLD_EXE_SUFFIX}.pdb" + LINK_FLAGS "/PDB:${CMAKE_CFG_INTDIR}/mysqld${MYSQLD_EXE_SUFFIX}.pdb") + IF(EMBED_MANIFESTS) MYSQL_EMBED_MANIFEST("mysqld" "asInvoker") ENDIF(EMBED_MANIFESTS) @@ -108,39 +121,49 @@ ENDIF(WITH_INNOBASE_STORAGE_ENGINE) ADD_DEPENDENCIES(mysqld GenError) +# NOTE CMake 2.4.6 creates strange dependencies between files in OUTPUT, +# so for now we only list one if more than one + # Sql Parser custom command ADD_CUSTOM_COMMAND( - SOURCE ${PROJECT_SOURCE_DIR}/sql/sql_yacc.yy - OUTPUT ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc - COMMAND bison.exe ARGS -y -p MYSQL --defines=sql_yacc.h - --output=sql_yacc.cc sql_yacc.yy - DEPENDS ${PROJECT_SOURCE_DIR}/sql/sql_yacc.yy) + OUTPUT ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h +# ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc + COMMAND bison.exe ARGS -y -p MYSQL --defines=sql_yacc.h + --output=sql_yacc.cc sql_yacc.yy + DEPENDS ${PROJECT_SOURCE_DIR}/sql/sql_yacc.yy) -ADD_CUSTOM_COMMAND( - OUTPUT ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h - COMMAND echo - DEPENDS ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc -) - -# Windows message file -ADD_CUSTOM_COMMAND( - SOURCE message.mc - OUTPUT message.rc message.h - COMMAND mc ARGS message.mc - DEPENDS message.mc) +IF(WIN32) + # Windows message file + ADD_CUSTOM_COMMAND( + OUTPUT ${PROJECT_SOURCE_DIR}/sql/message.h +# ${PROJECT_SOURCE_DIR}/sql/message.rc + COMMAND mc ARGS message.mc + DEPENDS ${PROJECT_SOURCE_DIR}/sql/message.mc) +ENDIF(WIN32) # Gen_lex_hash ADD_EXECUTABLE(gen_lex_hash gen_lex_hash.cc) -TARGET_LINK_LIBRARIES(gen_lex_hash dbug mysqlclient wsock32) +TARGET_LINK_LIBRARIES(gen_lex_hash debug dbug mysqlclient wsock32) GET_TARGET_PROPERTY(GEN_LEX_HASH_EXE gen_lex_hash LOCATION) ADD_CUSTOM_COMMAND( - OUTPUT ${PROJECT_SOURCE_DIR}/sql/lex_hash.h - COMMAND ${GEN_LEX_HASH_EXE} ARGS > lex_hash.h - DEPENDS ${GEN_LEX_HASH_EXE} -) + OUTPUT ${PROJECT_SOURCE_DIR}/sql/lex_hash.h + COMMAND ${GEN_LEX_HASH_EXE} ARGS > lex_hash.h + DEPENDS ${GEN_LEX_HASH_EXE}) + +ADD_CUSTOM_TARGET( + GenServerSource ALL + DEPENDS ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h +# ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc + ${PROJECT_SOURCE_DIR}/sql/message.h +# ${PROJECT_SOURCE_DIR}/sql/message.rc + ${PROJECT_SOURCE_DIR}/sql/lex_hash.h) + +ADD_DEPENDENCIES(mysqld GenServerSource) -ADD_DEPENDENCIES(mysqld gen_lex_hash) +# Remove the auto-generated files as part of 'Clean Solution' +SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES + "lex_hash.h;message.rc;message.h;sql_yacc.h;sql_yacc.cc") ADD_LIBRARY(udf_example MODULE udf_example.c udf_example.def) ADD_DEPENDENCIES(udf_example strings) -TARGET_LINK_LIBRARIES(udf_example wsock32) +TARGET_LINK_LIBRARIES(udf_example strings wsock32) diff --git a/sql/Makefile.am b/sql/Makefile.am index 36d066758bc..0081417d492 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -43,8 +43,7 @@ mysqld_LDADD = libndb.la \ @pstack_libs@ \ @mysql_plugin_libs@ \ $(LDADD) $(CXXLDFLAGS) $(WRAPLIBS) @LIBDL@ \ - $(yassl_libs) $(openssl_libs) \ - @MYSQLD_EXTRA_LIBS@ + $(yassl_libs) $(openssl_libs) @MYSQLD_EXTRA_LIBS@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ item_strfunc.h item_timefunc.h \ @@ -107,11 +106,11 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ rpl_utility.cc rpl_injector.cc rpl_rli.cc rpl_mi.cc \ rpl_reporting.cc \ sql_union.cc sql_derived.cc \ - client.c sql_client.cc mini_client_errors.c pack.c\ + sql_client.cc \ stacktrace.c repl_failsafe.h repl_failsafe.cc \ sql_olap.cc sql_view.cc \ gstream.cc spatial.cc sql_help.cc sql_cursor.cc \ - tztime.cc my_time.c my_user.c my_decimal.cc\ + tztime.cc my_decimal.cc\ sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \ sp_cache.cc parse_file.cc sql_trigger.cc \ event_scheduler.cc event_data_objects.cc \ @@ -120,6 +119,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ sql_builtin.cc sql_tablespace.cc partition_info.cc \ sql_servers.cc +nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c + libndb_la_CPPFLAGS= @ndbcluster_includes@ libndb_la_SOURCES= ha_ndbcluster.cc \ ha_ndbcluster_binlog.cc \ @@ -148,6 +149,7 @@ DISTCLEANFILES = $(EXTRA_PROGRAMS) MAINTAINERCLEANFILES = $(BUILT_MAINT_SRC) AM_YFLAGS = -d --verbose +# These are listed in 'nodist_mysqld_SOURCES' link_sources: rm -f mini_client_errors.c @LN_CP_F@ $(top_srcdir)/libmysql/errmsg.c mini_client_errors.c @@ -159,6 +161,7 @@ link_sources: @LN_CP_F@ $(top_srcdir)/sql-common/my_time.c my_time.c rm -f my_user.c @LN_CP_F@ $(top_srcdir)/sql-common/my_user.c my_user.c + echo timestamp > link_sources # This generates lex_hash.h # NOTE Built sources should depend on their sources not the tool diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 7b58c10079a..787b04c12c6 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -20,6 +20,10 @@ #include "event_db_repository.h" #include "sp_head.h" +/** + @addtogroup Event_Scheduler + @{ +*/ #define EVEX_MAX_INTERVAL_VALUE 1000000000L @@ -979,17 +983,18 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) DBUG_RETURN(TRUE); starts_null= table->field[ET_FIELD_STARTS]->is_null(); + my_bool not_used= FALSE; if (!starts_null) { table->field[ET_FIELD_STARTS]->get_date(&time, TIME_NO_ZERO_DATE); - starts= sec_since_epoch_TIME(&time); + starts= my_tz_OFFSET0->TIME_to_gmt_sec(&time,¬_used); } ends_null= table->field[ET_FIELD_ENDS]->is_null(); if (!ends_null) { table->field[ET_FIELD_ENDS]->get_date(&time, TIME_NO_ZERO_DATE); - ends= sec_since_epoch_TIME(&time); + ends= my_tz_OFFSET0->TIME_to_gmt_sec(&time,¬_used); } if (!table->field[ET_FIELD_INTERVAL_EXPR]->is_null()) @@ -1007,7 +1012,7 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) if (table->field[ET_FIELD_EXECUTE_AT]->get_date(&time, TIME_NO_ZERO_DATE)) DBUG_RETURN(TRUE); - execute_at= sec_since_epoch_TIME(&time); + execute_at= my_tz_OFFSET0->TIME_to_gmt_sec(&time,¬_used); } /* @@ -1039,7 +1044,7 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) { table->field[ET_FIELD_LAST_EXECUTED]->get_date(&time, TIME_NO_ZERO_DATE); - last_executed= sec_since_epoch_TIME(&time); + last_executed= my_tz_OFFSET0->TIME_to_gmt_sec(&time,¬_used); } last_executed_changed= FALSE; @@ -1644,7 +1649,7 @@ err: void Event_queue_element::mark_last_executed(THD *thd) { - thd->end_time(); + thd->set_current_time(); last_executed= (my_time_t) thd->query_start(); last_executed_changed= TRUE; @@ -2081,3 +2086,7 @@ event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b) return !sortcmp_lex_string(name, b->name, system_charset_info) && !sortcmp_lex_string(db, b->dbname, system_charset_info); } + +/** + @} (End of group Event_Scheduler) +*/ diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h index a8e7d1720ca..7a49d1597d6 100644 --- a/sql/event_data_objects.h +++ b/sql/event_data_objects.h @@ -15,6 +15,12 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/** + @addtogroup Event_Scheduler + @{ + + @file event_data_objects.h +*/ #define EVEX_GET_FIELD_FAILED -2 #define EVEX_BAD_PARAMS -5 @@ -280,5 +286,8 @@ event_basic_db_equal(LEX_STRING db, Event_basic *et); bool event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b); +/** + @} (End of group Event_Scheduler) +*/ #endif /* _EVENT_DATA_OBJECTS_H_ */ diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index a16a04739e2..705bd8b2704 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -20,6 +20,11 @@ #include "events.h" #include "sql_show.h" +/** + @addtogroup Event_Scheduler + @{ +*/ + static const TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] = { @@ -250,7 +255,7 @@ mysql_event_fill_row(THD *thd, if (!et->starts_null) { MYSQL_TIME time; - my_tz_UTC->gmt_sec_to_TIME(&time, et->starts); + my_tz_OFFSET0->gmt_sec_to_TIME(&time, et->starts); fields[ET_FIELD_STARTS]->set_notnull(); fields[ET_FIELD_STARTS]->store_time(&time, MYSQL_TIMESTAMP_DATETIME); @@ -259,7 +264,7 @@ mysql_event_fill_row(THD *thd, if (!et->ends_null) { MYSQL_TIME time; - my_tz_UTC->gmt_sec_to_TIME(&time, et->ends); + my_tz_OFFSET0->gmt_sec_to_TIME(&time, et->ends); fields[ET_FIELD_ENDS]->set_notnull(); fields[ET_FIELD_ENDS]->store_time(&time, MYSQL_TIMESTAMP_DATETIME); @@ -278,7 +283,7 @@ mysql_event_fill_row(THD *thd, fields[ET_FIELD_ENDS]->set_null(); MYSQL_TIME time; - my_tz_UTC->gmt_sec_to_TIME(&time, et->execute_at); + my_tz_OFFSET0->gmt_sec_to_TIME(&time, et->execute_at); fields[ET_FIELD_EXECUTE_AT]->set_notnull(); fields[ET_FIELD_EXECUTE_AT]-> @@ -390,8 +395,9 @@ Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table, } key_copy(key_buf, event_table->record[0], key_info, key_len); - if (!(ret= event_table->file->index_read(event_table->record[0], key_buf, - (key_part_map)1, HA_READ_PREFIX))) + if (!(ret= event_table->file->index_read_map(event_table->record[0], key_buf, + (key_part_map)1, + HA_READ_PREFIX))) { DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret)); do @@ -556,7 +562,7 @@ Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type, only creates a record on disk. @pre The thread handle has no open tables. - @param[in,out] THD + @param[in,out] thd THD @param[in] parse_data Parsed event definition @param[in] create_if_not TRUE if IF NOT EXISTS clause was provided to CREATE EVENT statement @@ -657,7 +663,7 @@ end: @param[in,out] thd thread handle @param[in] parse_data parsed event definition - @paran[in[ new_dbname not NULL if ALTER EVENT RENAME + @param[in] new_dbname not NULL if ALTER EVENT RENAME points at a new database name @param[in] new_name not NULL if ALTER EVENT RENAME points at a new event name @@ -812,7 +818,7 @@ end: @retval FALSE an event with such db/name key exists - @reval TRUE no record found or an error occured. + @retval TRUE no record found or an error occured. */ bool @@ -839,8 +845,8 @@ Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name, key_copy(key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx(table->record[0], 0, key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->index_read_idx_map(table->record[0], 0, key, HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { DBUG_PRINT("info", ("Row not found")); DBUG_RETURN(TRUE); @@ -1004,7 +1010,7 @@ update_timing_fields_for_event(THD *thd, if (update_last_executed) { MYSQL_TIME time; - my_tz_UTC->gmt_sec_to_TIME(&time, last_executed); + my_tz_OFFSET0->gmt_sec_to_TIME(&time, last_executed); fields[ET_FIELD_LAST_EXECUTED]->set_notnull(); fields[ET_FIELD_LAST_EXECUTED]->store_time(&time, @@ -1112,3 +1118,7 @@ Event_db_repository::check_system_tables(THD *thd) DBUG_RETURN(test(ret)); } + +/** + @} (End of group Event_Scheduler) +*/ diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h index b60d2ea7afc..ef778407d1e 100644 --- a/sql/event_db_repository.h +++ b/sql/event_db_repository.h @@ -15,8 +15,14 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* - @file +/** + @addtogroup Event_Scheduler + @{ + + @file event_db_repository.h + + Data Dictionary related operations of Event Scheduler. + This is a private header file of Events module. Please do not include it directly. All public declarations of Events module should be stored in events.h and event_data_objects.h. @@ -119,4 +125,7 @@ private: void operator=(Event_db_repository &); }; +/** + @} (End of group Event_Scheduler) +*/ #endif /* _EVENT_DB_REPOSITORY_H_ */ diff --git a/sql/event_queue.cc b/sql/event_queue.cc index 634cc764d74..898fc206c34 100644 --- a/sql/event_queue.cc +++ b/sql/event_queue.cc @@ -17,6 +17,10 @@ #include "event_queue.h" #include "event_data_objects.h" +/** + @addtogroup Event_Scheduler + @{ +*/ #define EVENT_QUEUE_INITIAL_SIZE 30 #define EVENT_QUEUE_EXTENT 30 @@ -52,8 +56,9 @@ execute_at.second_part is not considered during comparison */ -static int -event_queue_element_compare_q(void *vptr, uchar* a, uchar *b) +extern "C" int event_queue_element_compare_q(void *, uchar *, uchar *); + +int event_queue_element_compare_q(void *vptr, uchar* a, uchar *b) { my_time_t lhs = ((Event_queue_element *)a)->execute_at; my_time_t rhs = ((Event_queue_element *)b)->execute_at; @@ -550,7 +555,7 @@ Event_queue::get_top_for_execution_if_time(THD *thd, top= ((Event_queue_element*) queue_element(&queue, 0)); - thd->end_time(); /* Get current time */ + thd->set_current_time(); /* Get current time */ next_activation_at= top->execute_at; if (next_activation_at > thd->query_start()) @@ -740,7 +745,7 @@ Event_queue::dump_internal_status() printf("WOC : %s\n", waiting_on_cond? "YES":"NO"); MYSQL_TIME time; - my_tz_UTC->gmt_sec_to_TIME(&time, next_activation_at); + my_tz_OFFSET0->gmt_sec_to_TIME(&time, next_activation_at); if (time.year != 1970) printf("Next activation : %04d-%02d-%02d %02d:%02d:%02d\n", time.year, time.month, time.day, time.hour, time.minute, time.second); @@ -749,3 +754,7 @@ Event_queue::dump_internal_status() DBUG_VOID_RETURN; } + +/** + @} (End of group Event_Scheduler) +*/ diff --git a/sql/event_queue.h b/sql/event_queue.h index ac4a4f2bfd7..2870ecb4d0b 100644 --- a/sql/event_queue.h +++ b/sql/event_queue.h @@ -15,12 +15,26 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/** + + @addtogroup Event_Scheduler + @{ + + @file event_queue.h + + Queue of events awaiting execution. +*/ + class Event_basic; class Event_queue_element; class Event_queue_element_for_exec; class THD; +/** + Queue of active events awaiting execution. +*/ + class Event_queue { public: @@ -105,5 +119,8 @@ private: bool mutex_queue_data_attempting_lock; bool waiting_on_cond; }; +/** + @} (End of group Event_Scheduler) +*/ #endif /* _EVENT_QUEUE_H_ */ diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index c552b22e942..b03b51f1134 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -20,6 +20,11 @@ #include "event_queue.h" #include "event_db_repository.h" +/** + @addtogroup Event_Scheduler + @{ +*/ + #ifdef __GNUC__ #if __GNUC__ >= 2 #define SCHED_FUNC __FUNCTION__ @@ -283,8 +288,7 @@ Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event) 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)); + DBUG_PRINT("info", ("Time is %ld, THD: 0x%lx", (long) my_time(0), (long) thd)); if (res) goto end; @@ -791,3 +795,7 @@ Event_scheduler::dump_internal_status() DBUG_VOID_RETURN; } + +/** + @} (End of group Event_Scheduler) +*/ diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h index eba66c68d42..0be93a65d33 100644 --- a/sql/event_scheduler.h +++ b/sql/event_scheduler.h @@ -16,10 +16,18 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** + @addtogroup Event_Scheduler + @{ +*/ +/** @file - This file is internal to Events module. Please do not include it directly. - All public declarations of Events module are in events.h and - event_data_objects.h. + + Declarations of the scheduler thread class + and related functionality. + + This file is internal to Event_Scheduler module. Please do not + include it directly. All public declarations of Event_Scheduler + module are in events.h and event_data_objects.h. */ @@ -140,4 +148,8 @@ private: void operator=(Event_scheduler &); }; +/** + @} (End of group Event_Scheduler) +*/ + #endif /* _EVENT_SCHEDULER_H_ */ diff --git a/sql/events.cc b/sql/events.cc index e48daeca63d..5246bccc388 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -21,6 +21,11 @@ #include "event_scheduler.h" #include "sp_head.h" // for Stored_program_creation_ctx +/** + @addtogroup Event_Scheduler + @{ +*/ + /* TODO list : - CREATE EVENT should not go into binary log! Does it now? The SQL statements @@ -717,7 +722,8 @@ send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol) protocol->store(et->name.str, et->name.length, system_charset_info); protocol->store(sql_mode.str, sql_mode.length, system_charset_info); protocol->store(tz_name->ptr(), tz_name->length(), system_charset_info); - protocol->store(show_str.c_ptr(), show_str.length(), &my_charset_bin); + protocol->store(show_str.c_ptr(), show_str.length(), + et->creation_ctx->get_client_cs()); protocol->store(et->creation_ctx->get_client_cs()->csname, strlen(et->creation_ctx->get_client_cs()->csname), system_charset_info); @@ -789,8 +795,7 @@ Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name) Check access rights and fill INFORMATION_SCHEMA.events table. @param[in,out] thd Thread context - @param[in] table The temporary table to fill. - cond Unused + @param[in] tables The temporary table to fill. In MySQL INFORMATION_SCHEMA tables are temporary tables that are created and filled on demand. In this function, we fill @@ -1186,3 +1191,7 @@ end: DBUG_RETURN(ret); } + +/** + @} (End of group Event_Scheduler) +*/ diff --git a/sql/events.h b/sql/events.h index 1b99b072fd7..2bc87517748 100644 --- a/sql/events.h +++ b/sql/events.h @@ -15,9 +15,14 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* - @file - A public interface of Events Scheduler module. +/** + @defgroup Event_Scheduler Event Scheduler + @ingroup Runtime_Environment + @{ + + @file events.h + + A public interface of Events_Scheduler module. */ class Event_parse_data; @@ -42,7 +47,7 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs); /** - @class Events -- a facade to the functionality of the Event Scheduler. + @brief A facade to the functionality of the Event Scheduler. Every public operation against the scheduler has to be executed via the interface provided by a static method of this class. No instance of this @@ -152,5 +157,8 @@ private: void operator=(Events &); }; +/** + @} (end of group Event Scheduler) +*/ #endif /* _EVENT_H_ */ diff --git a/sql/field.cc b/sql/field.cc index d06d94af3a7..fa93454c757 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1360,6 +1360,84 @@ bool Field::send_binary(Protocol *protocol) } +/** + Check to see if field size is compatible with destination. + + This method is used in row-based replication to verify that the slave's + field size is less than or equal to the master's field size. The + encoded field metadata (from the master or source) is decoded and compared + to the size of this field (the slave or destination). + + @param field_metadata Encoded size in field metadata + + @retval 0 if this field's size is < the source field's size + @retval 1 if this field's size is >= the source field's size +*/ +int Field::compatible_field_size(uint field_metadata) +{ + uint const source_size= pack_length_from_metadata(field_metadata); + uint const destination_size= row_pack_length(); + return (source_size <= destination_size); +} + + +int Field::store(const char *to, uint length, CHARSET_INFO *cs, + enum_check_fields check_level) +{ + int res; + enum_check_fields old_check_level= table->in_use->count_cuted_fields; + table->in_use->count_cuted_fields= check_level; + res= store(to, length, cs); + table->in_use->count_cuted_fields= old_check_level; + return res; +} + + +/** + Unpack a field from row data. + + This method is used to unpack a field from a master whose size + of the field is less than that of the slave. + + @param to Destination of the data + @param from Source of the data + @param param_data Pack length of the field data + + @return New pointer into memory based on from + length of the data +*/ +const uchar *Field::unpack(uchar* to, + const uchar *from, + uint param_data) +{ + uint length=pack_length(); + int from_type= 0; + /* + If from length is > 255, it has encoded data in the upper bits. Need + to mask it out. + */ + if (param_data > 255) + { + from_type= (param_data & 0xff00) >> 8U; // real_type. + param_data= param_data & 0x00ff; // length. + } + uint len= (param_data && (param_data < length)) ? + param_data : length; + /* + If the length is the same, use old unpack method. + If the param_data is 0, use the old unpack method. + This is possible if the table map was generated from a down-level + master or if the data was not available on the master. + If the real_types are not the same, use the old unpack method. + */ + if ((length == param_data) || + (param_data == 0) || + (from_type != real_type())) + return(unpack(to, from)); + memcpy(to, from, param_data > length ? length : len); + return from+len; +} + + my_decimal *Field::val_decimal(my_decimal *decimal) { /* This never have to be called */ @@ -2316,6 +2394,7 @@ Field_new_decimal::Field_new_decimal(uchar *ptr_arg, unireg_check_arg, field_name_arg, dec_arg, zero_arg, unsigned_arg) { precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg); + set_if_smaller(precision, DECIMAL_MAX_PRECISION); DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) && (dec <= DECIMAL_MAX_SCALE)); bin_size= my_decimal_get_binary_size(precision, dec); @@ -2332,6 +2411,7 @@ Field_new_decimal::Field_new_decimal(uint32 len_arg, NONE, name, dec_arg, 0, unsigned_arg) { precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg); + set_if_smaller(precision, DECIMAL_MAX_PRECISION); DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) && (dec <= DECIMAL_MAX_SCALE)); bin_size= my_decimal_get_binary_size(precision, dec); @@ -2631,6 +2711,76 @@ void Field_new_decimal::sql_type(String &str) const } +/** + Save the field metadata for new decimal fields. + + Saves the precision in the first byte and decimals() in the second + byte of the field metadata array at index of *metadata_ptr and + *(metadata_ptr + 1). + + @param metadata_ptr First byte of field metadata + + @returns number of bytes written to metadata_ptr +*/ +int Field_new_decimal::do_save_field_metadata(uchar *metadata_ptr) +{ + *metadata_ptr= precision; + *(metadata_ptr + 1)= decimals(); + return 2; +} + + +/** + Returns the number of bytes field uses in row-based replication + row packed size. + + This method is used in row-based replication to determine the number + of bytes that the field consumes in the row record format. This is + used to skip fields in the master that do not exist on the slave. + + @param field_metadata Encoded size in field metadata + + @returns The size of the field based on the field metadata. +*/ +uint Field_new_decimal::pack_length_from_metadata(uint field_metadata) +{ + uint const source_precision= (field_metadata >> 8U) & 0x00ff; + uint const source_decimal= field_metadata & 0x00ff; + uint const source_size= my_decimal_get_binary_size(source_precision, + source_decimal); + return (source_size); +} + + +/** + Check to see if field size is compatible with destination. + + This method is used in row-based replication to verify that the slave's + field size is less than or equal to the master's field size. The + encoded field metadata (from the master or source) is decoded and compared + to the size of this field (the slave or destination). + + @param field_metadata Encoded size in field metadata + + @retval 0 if this field's size is < the source field's size + @retval 1 if this field's size is >= the source field's size +*/ +int Field_new_decimal::compatible_field_size(uint field_metadata) +{ + int compatible= 0; + uint const source_precision= (field_metadata >> 8U) & 0x00ff; + uint const source_decimal= field_metadata & 0x00ff; + uint const source_size= my_decimal_get_binary_size(source_precision, + source_decimal); + uint const destination_size= row_pack_length(); + compatible= (source_size <= destination_size); + if (compatible) + compatible= (source_precision <= precision) && + (source_decimal <= decimals()); + return (compatible); +} + + uint Field_new_decimal::is_equal(Create_field *new_field) { return ((new_field->sql_type == real_type()) && @@ -2643,6 +2793,54 @@ uint Field_new_decimal::is_equal(Create_field *new_field) } +/** + Unpack a decimal field from row data. + + This method is used to unpack a decimal or numeric field from a master + whose size of the field is less than that of the slave. + + @param to Destination of the data + @param from Source of the data + @param param_data Precision (upper) and decimal (lower) values + + @return New pointer into memory based on from + length of the data +*/ +const uchar *Field_new_decimal::unpack(uchar* to, + const uchar *from, + uint param_data) +{ + uint from_precision= (param_data & 0xff00) >> 8U; + uint from_decimal= param_data & 0x00ff; + uint length=pack_length(); + uint from_pack_len= my_decimal_get_binary_size(from_precision, from_decimal); + uint len= (param_data && (from_pack_len < length)) ? + from_pack_len : length; + if ((from_pack_len && (from_pack_len < length)) || + (from_precision < precision) || + (from_decimal < decimals())) + { + /* + If the master's data is smaller than the slave, we need to convert + the binary to decimal then resize the decimal converting it back to + a decimal and write that to the raw data buffer. + */ + decimal_digit_t dec_buf[DECIMAL_MAX_PRECISION]; + decimal_t dec; + dec.len= from_precision; + dec.buf= dec_buf; + /* + Note: bin2decimal does not change the length of the field. So it is + just the first step the resizing operation. The second step does the + resizing using the precision and decimals from the slave. + */ + bin2decimal((uchar *)from, &dec, from_precision, from_decimal); + decimal2bin(&dec, to, precision, decimals()); + } + else + memcpy(to, from, len); // Sizes are the same, just copy the data. + return from+len; +} + /**************************************************************************** ** tiny int ****************************************************************************/ @@ -3982,6 +4180,22 @@ bool Field_float::send_binary(Protocol *protocol) } +/** + Save the field metadata for float fields. + + Saves the pack length in the first byte. + + @param metadata_ptr First byte of field metadata + + @returns number of bytes written to metadata_ptr +*/ +int Field_float::do_save_field_metadata(uchar *metadata_ptr) +{ + *metadata_ptr= pack_length(); + return 1; +} + + void Field_float::sql_type(String &res) const { if (dec == NOT_FIXED_DEC) @@ -4299,6 +4513,23 @@ void Field_double::sort_string(uchar *to,uint length __attribute__((unused))) } +/** + Save the field metadata for double fields. + + Saves the pack length in the first byte of the field metadata array + at index of *metadata_ptr. + + @param metadata_ptr First byte of field metadata + + @returns number of bytes written to metadata_ptr +*/ +int Field_double::do_save_field_metadata(uchar *metadata_ptr) +{ + *metadata_ptr= pack_length(); + return 1; +} + + void Field_double::sql_type(String &res) const { CHARSET_INFO *cs=res.charset(); @@ -4473,15 +4704,7 @@ int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) error= 1; } } - -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - { - int4store(ptr,tmp); - } - else -#endif - longstore(ptr,tmp); + store_timestamp(tmp); return error; } @@ -4541,19 +4764,10 @@ int Field_timestamp::store(longlong nr, bool unsigned_val) WARN_DATA_TRUNCATED, nr, MYSQL_TIMESTAMP_DATETIME, 1); -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - { - int4store(ptr,timestamp); - } - else -#endif - longstore(ptr,(uint32) timestamp); - + store_timestamp(timestamp); return error; } - double Field_timestamp::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; @@ -4567,6 +4781,7 @@ longlong Field_timestamp::val_int(void) MYSQL_TIME time_tmp; THD *thd= table ? table->in_use : current_thd; + thd->time_zone_used= 1; #ifdef WORDS_BIGENDIAN if (table && table->s->db_low_byte_first) temp=uint4korr(ptr); @@ -4578,7 +4793,6 @@ longlong Field_timestamp::val_int(void) return(0); /* purecov: inspected */ thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, (my_time_t)temp); - thd->time_zone_used= 1; return time_tmp.year * LL(10000000000) + time_tmp.month * LL(100000000) + time_tmp.day * 1000000L + time_tmp.hour * 10000L + @@ -4598,6 +4812,7 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) to= (char*) val_buffer->ptr(); val_buffer->length(field_length); + thd->time_zone_used= 1; #ifdef WORDS_BIGENDIAN if (table && table->s->db_low_byte_first) temp=uint4korr(ptr); @@ -4613,7 +4828,6 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) val_buffer->set_charset(&my_charset_bin); // Safety thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,(my_time_t)temp); - thd->time_zone_used= 1; temp= time_tmp.year % 100; if (temp < YY_PART_YEAR - 1) @@ -4663,6 +4877,7 @@ bool Field_timestamp::get_date(MYSQL_TIME *ltime, uint fuzzydate) { long temp; THD *thd= table ? table->in_use : current_thd; + thd->time_zone_used= 1; #ifdef WORDS_BIGENDIAN if (table && table->s->db_low_byte_first) temp=uint4korr(ptr); @@ -4678,7 +4893,6 @@ bool Field_timestamp::get_date(MYSQL_TIME *ltime, uint fuzzydate) else { thd->variables.time_zone->gmt_sec_to_TIME(ltime, (my_time_t)temp); - thd->time_zone_used= 1; } return 0; } @@ -4748,14 +4962,7 @@ void Field_timestamp::set_time() THD *thd= table ? table->in_use : current_thd; long tmp= (long) thd->query_start(); set_notnull(); -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - { - int4store(ptr,tmp); - } - else -#endif - longstore(ptr,tmp); + store_timestamp(tmp); } /**************************************************************************** @@ -5392,7 +5599,8 @@ int Field_newdate::store(const char *from,uint len,CHARSET_INFO *cs) else { tmp= l_time.day + l_time.month*32 + l_time.year*16*32; - if (!error && (ret != MYSQL_TIMESTAMP_DATE)) + if (!error && (ret != MYSQL_TIMESTAMP_DATE) && + thd->count_cuted_fields != CHECK_FIELD_IGNORE) error= 3; // Datetime was cut (note) } @@ -6090,12 +6298,12 @@ uint Field::is_equal(Create_field *new_field) /* If one of the fields is binary and the other one isn't return 1 else 0 */ -bool Field_str::compare_str_field_flags(Create_field *new_field, uint32 flags) +bool Field_str::compare_str_field_flags(Create_field *new_field, uint32 flag_arg) { return (((new_field->flags & (BINCMP_FLAG | BINARY_FLAG)) && - !(flags & (BINCMP_FLAG | BINARY_FLAG))) || + !(flag_arg & (BINCMP_FLAG | BINARY_FLAG))) || (!(new_field->flags & (BINCMP_FLAG | BINARY_FLAG)) && - (flags & (BINCMP_FLAG | BINARY_FLAG)))); + (flag_arg & (BINCMP_FLAG | BINARY_FLAG)))); } @@ -6291,6 +6499,38 @@ uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length) } +/** + Unpack a string field from row data. + + This method is used to unpack a string field from a master whose size + of the field is less than that of the slave. Note that there can be a + variety of field types represented with this class. Certain types like + ENUM or SET are processed differently. Hence, the upper byte of the + @c param_data argument contains the result of field->real_type() from + the master. + + @param to Destination of the data + @param from Source of the data + @param param_data Real type (upper) and length (lower) values + + @return New pointer into memory based on from + length of the data +*/ +const uchar *Field_string::unpack(uchar *to, + const uchar *from, + uint param_data) +{ + uint from_len= param_data & 0x00ff; // length. + uint length= 0; + uint f_length; + f_length= (from_len < field_length) ? from_len : field_length; + DBUG_ASSERT(f_length <= 255); + length= (uint) *from++; + bitmap_set_bit(table->write_set,field_index); + store((const char *)from, length, system_charset_info); + return from+length; +} + + const uchar *Field_string::unpack(uchar *to, const uchar *from) { uint length; @@ -6307,6 +6547,25 @@ const uchar *Field_string::unpack(uchar *to, const uchar *from) } +/** + Save the field metadata for string fields. + + Saves the real type in the first byte and the field length in the + second byte of the field metadata array at index of *metadata_ptr and + *(metadata_ptr + 1). + + @param metadata_ptr First byte of field metadata + + @returns number of bytes written to metadata_ptr +*/ +int Field_string::do_save_field_metadata(uchar *metadata_ptr) +{ + *metadata_ptr= real_type(); + *(metadata_ptr + 1)= field_length; + return 2; +} + + /* Compare two packed keys @@ -6459,6 +6718,24 @@ Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table, const uint Field_varstring::MAX_SIZE= UINT_MAX16; +/** + Save the field metadata for varstring fields. + + Saves the field length in the first byte. Note: may consume + 2 bytes. Caller must ensure second byte is contiguous with + first byte (e.g. array index 0,1). + + @param metadata_ptr First byte of field metadata + + @returns number of bytes written to metadata_ptr +*/ +int Field_varstring::do_save_field_metadata(uchar *metadata_ptr) +{ + char *ptr= (char *)metadata_ptr; + int2store(ptr, field_length); + return 2; +} + int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; @@ -6782,6 +7059,44 @@ uchar *Field_varstring::pack_key_from_key_image(uchar *to, const uchar *from, } +/** + Unpack a varstring field from row data. + + This method is used to unpack a varstring field from a master + whose size of the field is less than that of the slave. + + @param to Destination of the data + @param from Source of the data + @param param_data Length bytes from the master's field data + + @return New pointer into memory based on from + length of the data +*/ +const uchar *Field_varstring::unpack(uchar *to, + const uchar *from, + uint param_data) +{ + uint length; + uint l_bytes= (param_data && (param_data < field_length)) ? + (param_data <= 255) ? 1 : 2 : length_bytes; + if (l_bytes == 1) + { + to[0]= *from++; + length= to[0]; + if (length_bytes == 2) + to[1]= 0; + } + else + { + length= uint2korr(from); + to[0]= *from++; + to[1]= *from++; + } + if (length) + memcpy(to+ length_bytes, from, length); + return from+length; +} + + /* unpack field packed with Field_varstring::pack() */ @@ -7005,7 +7320,10 @@ Field_blob::Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, } -void Field_blob::store_length(uchar *i_ptr, uint i_packlength, uint32 i_number) +void Field_blob::store_length(uchar *i_ptr, + uint i_packlength, + uint32 i_number, + bool low_byte_first) { switch (i_packlength) { case 1: @@ -7013,7 +7331,7 @@ void Field_blob::store_length(uchar *i_ptr, uint i_packlength, uint32 i_number) break; case 2: #ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) + if (low_byte_first) { int2store(i_ptr,(unsigned short) i_number); } @@ -7026,7 +7344,7 @@ void Field_blob::store_length(uchar *i_ptr, uint i_packlength, uint32 i_number) break; case 4: #ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) + if (low_byte_first) { int4store(i_ptr,i_number); } @@ -7037,7 +7355,7 @@ void Field_blob::store_length(uchar *i_ptr, uint i_packlength, uint32 i_number) } -uint32 Field_blob::get_length(const uchar *pos) +uint32 Field_blob::get_length(const uchar *pos, bool low_byte_first) { switch (packlength) { case 1: @@ -7046,7 +7364,7 @@ uint32 Field_blob::get_length(const uchar *pos) { uint16 tmp; #ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) + if (low_byte_first) tmp=sint2korr(pos); else #endif @@ -7059,7 +7377,7 @@ uint32 Field_blob::get_length(const uchar *pos) { uint32 tmp; #ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) + if (low_byte_first) tmp=uint4korr(pos); else #endif @@ -7384,6 +7702,23 @@ int Field_blob::key_cmp(const uchar *a,const uchar *b) } +/** + Save the field metadata for blob fields. + + Saves the pack length in the first byte of the field metadata array + at index of *metadata_ptr. + + @param metadata_ptr First byte of field metadata + + @returns number of bytes written to metadata_ptr +*/ +int Field_blob::do_save_field_metadata(uchar *metadata_ptr) +{ + *metadata_ptr= pack_length_no_ptr(); + return 1; +} + + uint32 Field_blob::sort_length() const { return (uint32) (current_thd->variables.max_sort_length + @@ -7461,10 +7796,8 @@ uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length) uint32 length=get_length(); // Length of from string if (length > max_length) { - ptr=to; length=max_length; - store_length(length); // Store max length - ptr= (uchar*) from; + store_length(to,packlength,length,TRUE); } else memcpy(to,from,packlength); // Copy length @@ -7478,10 +7811,33 @@ uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length) } +/** + Unpack a blob field from row data. + + This method is used to unpack a blob field from a master whose size of + the field is less than that of the slave. Note: This method is included + to satisfy inheritance rules, but is not needed for blob fields. It + simply is used as a pass-through to the original unpack() method for + blob fields. + + @param to Destination of the data + @param from Source of the data + @param param_data not used + + @return New pointer into memory based on from + length of the data +*/ +const uchar *Field_blob::unpack(uchar *to, + const uchar *from, + uint param_data) +{ + return unpack(to, from); +} + + const uchar *Field_blob::unpack(uchar *to, const uchar *from) { - memcpy(to,from,packlength); uint32 length=get_length(from); + memcpy(to,from,packlength); from+=packlength; if (length) memcpy_fixed(to+packlength, &from, sizeof(from)); @@ -7541,7 +7897,7 @@ uchar *Field_blob::pack_key(uchar *to, const uchar *from, uint max_length) { uchar *save= ptr; ptr= (uchar*) from; - uint32 length=get_length(); // Length of from string + uint32 length=get_length(); // Length of from string uint local_char_length= ((field_charset->mbmaxlen > 1) ? max_length/field_charset->mbmaxlen : max_length); if (length) @@ -7886,8 +8242,11 @@ int Field_enum::store(longlong nr, bool unsigned_val) if ((ulonglong) nr > typelib->count || nr == 0) { set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); - nr=0; - error=1; + if (nr != 0 || table->in_use->count_cuted_fields) + { + nr= 0; + error= 1; + } } store_type((ulonglong) (uint) nr); return error; @@ -7946,6 +8305,25 @@ longlong Field_enum::val_int(void) } +/** + Save the field metadata for enum fields. + + Saves the real type in the first byte and the pack length in the + second byte of the field metadata array at index of *metadata_ptr and + *(metadata_ptr + 1). + + @param metadata_ptr First byte of field metadata + + @returns number of bytes written to metadata_ptr +*/ +int Field_enum::do_save_field_metadata(uchar *metadata_ptr) +{ + *metadata_ptr= real_type(); + *(metadata_ptr + 1)= pack_length(); + return 2; +} + + String *Field_enum::val_str(String *val_buffer __attribute__((unused)), String *val_ptr) { @@ -8482,6 +8860,77 @@ uint Field_bit::get_key_image(uchar *buff, uint length, imagetype type_arg) } +/** + Save the field metadata for bit fields. + + Saves the bit length in the first byte and bytes in record in the + second byte of the field metadata array at index of *metadata_ptr and + *(metadata_ptr + 1). + + @param metadata_ptr First byte of field metadata + + @returns number of bytes written to metadata_ptr +*/ +int Field_bit::do_save_field_metadata(uchar *metadata_ptr) +{ + *metadata_ptr= bit_len; + *(metadata_ptr + 1)= bytes_in_rec; + return 2; +} + + +/** + Returns the number of bytes field uses in row-based replication + row packed size. + + This method is used in row-based replication to determine the number + of bytes that the field consumes in the row record format. This is + used to skip fields in the master that do not exist on the slave. + + @param field_metadata Encoded size in field metadata + + @returns The size of the field based on the field metadata. +*/ +uint Field_bit::pack_length_from_metadata(uint field_metadata) +{ + uint const from_len= (field_metadata >> 8U) & 0x00ff; + uint const from_bit_len= field_metadata & 0x00ff; + uint const source_size= from_len + ((from_bit_len > 0) ? 1 : 0); + return (source_size); +} + + +/** + Check to see if field size is compatible with destination. + + This method is used in row-based replication to verify that the slave's + field size is less than or equal to the master's field size. The + encoded field metadata (from the master or source) is decoded and compared + to the size of this field (the slave or destination). + + @param field_metadata Encoded size in field metadata + + @retval 0 if this field's size is < the source field's size + @retval 1 if this field's size is >= the source field's size +*/ +int Field_bit::compatible_field_size(uint field_metadata) +{ + int compatible= 0; + uint const source_size= pack_length_from_metadata(field_metadata); + uint const destination_size= row_pack_length(); + uint const from_bit_len= field_metadata & 0x00ff; + uint const from_len= (field_metadata >> 8U) & 0x00ff; + if ((bit_len == 0) || (from_bit_len == 0)) + compatible= (source_size <= destination_size); + else if (from_bit_len > bit_len) + compatible= (from_len < bytes_in_rec); + else + compatible= ((from_bit_len <= bit_len) && (from_len <= bytes_in_rec)); + return (compatible); +} + + + void Field_bit::sql_type(String &res) const { CHARSET_INFO *cs= res.charset(); @@ -8525,6 +8974,58 @@ uchar *Field_bit::pack(uchar *to, const uchar *from, uint max_length) } +/** + Unpack a bit field from row data. + + This method is used to unpack a bit field from a master whose size + of the field is less than that of the slave. + + @param to Destination of the data + @param from Source of the data + @param param_data Bit length (upper) and length (lower) values + + @return New pointer into memory based on from + length of the data +*/ +const uchar *Field_bit::unpack(uchar *to, + const uchar *from, + uint param_data) +{ + uint const from_len= (param_data >> 8U) & 0x00ff; + uint const from_bit_len= param_data & 0x00ff; + /* + If the master and slave have the same sizes, then use the old + unpack() method. + */ + if ((from_bit_len == bit_len) && + (from_len == bytes_in_rec)) + return(unpack(to, from)); + /* + We are converting a smaller bit field to a larger one here. + To do that, we first need to construct a raw value for the original + bit value stored in the from buffer. Then that needs to be converted + to the larger field then sent to store() for writing to the field. + Lastly the odd bits need to be masked out if the bytes_in_rec > 0. + Otherwise stray bits can cause spurious values. + */ + uint new_len= (field_length + 7) / 8; + char *value= (char *)my_alloca(new_len); + bzero(value, new_len); + uint len= from_len + ((from_bit_len > 0) ? 1 : 0); + memcpy(value + (new_len - len), from, len); + /* + Mask out the unused bits in the partial byte. + TODO: Add code to the master to always mask these bits and remove + the following. + */ + if ((from_bit_len > 0) && (from_len > 0)) + value[new_len - len]= value[new_len - len] & ((1U << from_bit_len) - 1); + bitmap_set_bit(table->write_set,field_index); + store(value, new_len, system_charset_info); + my_afree(value); + return from + len; +} + + const uchar *Field_bit::unpack(uchar *to, const uchar *from) { if (bit_len > 0) @@ -9553,4 +10054,3 @@ Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code, field_name); } } - diff --git a/sql/field.h b/sql/field.h index e67609dbcb2..8aad6783291 100644 --- a/sql/field.h +++ b/sql/field.h @@ -100,6 +100,8 @@ public: virtual int store(longlong nr, bool unsigned_val)=0; virtual int store_decimal(const my_decimal *d)=0; virtual int store_time(MYSQL_TIME *ltime, timestamp_type t_type); + int store(const char *to, uint length, CHARSET_INFO *cs, + enum_check_fields check_level); virtual double val_real(void)=0; virtual longlong val_int(void)=0; virtual my_decimal *val_decimal(my_decimal *); @@ -149,6 +151,24 @@ public: table, which is located on disk). */ virtual uint32 pack_length_in_rec() const { return pack_length(); } + virtual int compatible_field_size(uint field_metadata); + virtual uint pack_length_from_metadata(uint field_metadata) + { return field_metadata; } + /* + This method is used to return the size of the data in a row-based + replication row record. The default implementation of returning 0 is + designed to allow fields that do not use metadata to return TRUE (1) + from compatible_field_size() which uses this function in the comparison. + The default value for field metadata for fields that do not have + metadata is 0. Thus, 0 == 0 means the fields are compatible in size. + + Note: While most classes that override this method return pack_length(), + the classes Field_string, Field_varstring, and Field_blob return + field_length + 1, field_length, and pack_length_no_ptr() respectfully. + */ + virtual uint row_pack_length() { return 0; } + virtual int save_field_metadata(uchar *first_byte) + { return do_save_field_metadata(first_byte); } /* data_length() return the "real size" of the data in memory. @@ -273,9 +293,9 @@ public: if (null_ptr) null_ptr=ADD_TO_PTR(null_ptr,ptr_diff,uchar*); } - inline void get_image(uchar *buff,uint length, CHARSET_INFO *cs) + virtual void get_image(uchar *buff, uint length, CHARSET_INFO *cs) { memcpy(buff,ptr,length); } - inline void set_image(const uchar *buff,uint length, CHARSET_INFO *cs) + virtual void set_image(const uchar *buff,uint length, CHARSET_INFO *cs) { memcpy(ptr,buff,length); } @@ -343,6 +363,7 @@ public: memcpy(to,from,length); return to+length; } + virtual const uchar *unpack(uchar* to, const uchar *from, uint param_data); virtual const uchar *unpack(uchar* to, const uchar *from) { uint length=pack_length(); @@ -460,6 +481,19 @@ private: overridden by subclasses. */ virtual size_t do_last_null_byte() const; + +/** + Retrieve the field metadata for fields. + + This default implementation returns 0 and saves 0 in the metadata_ptr + value. + + @param metadata_ptr First byte of field metadata + + @returns 0 no bytes written. +*/ + virtual int do_save_field_metadata(uchar *metadata_ptr) + { return 0; } }; @@ -586,6 +620,8 @@ public: /* New decimal/numeric field which use fixed point arithmetic */ class Field_new_decimal :public Field_num { +private: + int do_save_field_metadata(uchar *first_byte); public: /* The maximum number of decimal digits can be stored */ uint precision; @@ -625,7 +661,11 @@ public: uint32 max_display_length() { return field_length; } uint size_of() const { return sizeof(*this); } uint32 pack_length() const { return (uint32) bin_size; } + uint pack_length_from_metadata(uint field_metadata); + uint row_pack_length() { return pack_length(); } + int compatible_field_size(uint field_metadata); uint is_equal(Create_field *new_field); + virtual const uchar *unpack(uchar* to, const uchar *from, uint param_data); }; @@ -830,7 +870,10 @@ public: int cmp(const uchar *,const uchar *); void sort_string(uchar *buff,uint length); uint32 pack_length() const { return sizeof(float); } + uint row_pack_length() { return pack_length(); } void sql_type(String &str) const; +private: + int do_save_field_metadata(uchar *first_byte); }; @@ -867,7 +910,10 @@ public: int cmp(const uchar *,const uchar *); void sort_string(uchar *buff,uint length); uint32 pack_length() const { return sizeof(double); } + uint row_pack_length() { return pack_length(); } void sql_type(String &str) const; +private: + int do_save_field_metadata(uchar *first_byte); }; @@ -950,6 +996,17 @@ public: longget(tmp,ptr); return tmp; } + inline void store_timestamp(my_time_t timestamp) + { +#ifdef WORDS_BIGENDIAN + if (table && table->s->db_low_byte_first) + { + int4store(ptr,timestamp); + } + else +#endif + longstore(ptr,(uint32) timestamp); + } bool get_date(MYSQL_TIME *ltime,uint fuzzydate); bool get_time(MYSQL_TIME *ltime); timestamp_auto_set_type get_auto_set_type() const; @@ -1162,7 +1219,11 @@ public: void sort_string(uchar *buff,uint length); void sql_type(String &str) const; uchar *pack(uchar *to, const uchar *from, uint max_length=~(uint) 0); + virtual const uchar *unpack(uchar* to, const uchar *from, uint param_data); const uchar *unpack(uchar* to, const uchar *from); + uint pack_length_from_metadata(uint field_metadata) + { return (field_metadata & 0x00ff); } + uint row_pack_length() { return (field_length + 1); } int pack_cmp(const uchar *a,const uchar *b,uint key_length, my_bool insert_or_update); int pack_cmp(const uchar *b,uint key_length,my_bool insert_or_update); @@ -1174,6 +1235,8 @@ public: { return charset() == &my_charset_bin ? FALSE : TRUE; } Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type); virtual uint get_key_image(uchar *buff,uint length, imagetype type); +private: + int do_save_field_metadata(uchar *first_byte); }; @@ -1209,6 +1272,7 @@ public: enum_field_types type() const { return MYSQL_TYPE_VARCHAR; } enum ha_base_keytype key_type() const; + uint row_pack_length() { return field_length; } bool zero_pack() const { return 0; } int reset(void) { bzero(ptr,field_length+length_bytes); return 0; } uint32 pack_length() const { return (uint32) field_length+length_bytes; } @@ -1238,6 +1302,7 @@ public: uchar *pack_key(uchar *to, const uchar *from, uint max_length); uchar *pack_key_from_key_image(uchar* to, const uchar *from, uint max_length); + virtual const uchar *unpack(uchar* to, const uchar *from, uint param_data); const uchar *unpack(uchar* to, const uchar *from); const uchar *unpack_key(uchar* to, const uchar *from, uint max_length); int pack_cmp(const uchar *a, const uchar *b, uint key_length, @@ -1259,6 +1324,8 @@ public: uint new_null_bit); uint is_equal(Create_field *new_field); void hash(ulong *nr, ulong *nr2); +private: + int do_save_field_metadata(uchar *first_byte); }; @@ -1293,6 +1360,9 @@ public: l_char_length <= 16777215 ? 3 : 4; } } + Field_blob(uint32 packlength_arg) + :Field_longstr((uchar*) 0, 0, (uchar*) "", 0, NONE, "temp", system_charset_info), + packlength(packlength_arg) {} enum_field_types type() const { return MYSQL_TYPE_BLOB;} enum ha_base_keytype key_type() const { return binary() ? HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2; } @@ -1314,6 +1384,18 @@ public: void sort_string(uchar *buff,uint length); uint32 pack_length() const { return (uint32) (packlength+table->s->blob_ptr_size); } + + /** + Return the packed length without the pointer size added. + + This is used to determine the size of the actual data in the row + buffer. + + @returns The length of the raw data itself without the pointer. + */ + uint32 pack_length_no_ptr() const + { return (uint32) (packlength); } + uint row_pack_length() { return pack_length_no_ptr(); } uint32 sort_length() const; inline uint32 max_data_length() const { @@ -1324,15 +1406,32 @@ public: #ifndef WORDS_BIGENDIAN static #endif - void store_length(uchar *i_ptr, uint i_packlength, uint32 i_number); + void store_length(uchar *i_ptr, uint i_packlength, uint32 i_number, bool low_byte_first); + void store_length(uchar *i_ptr, uint i_packlength, uint32 i_number) + { + store_length(i_ptr, i_packlength, i_number, table->s->db_low_byte_first); + } inline void store_length(uint32 number) { store_length(ptr, packlength, number); } - - inline uint32 get_length(uint row_offset=0) - { return get_length(ptr+row_offset); } - uint32 get_length(const uchar *ptr); + + /** + Return the packed length plus the length of the data. + + This is used to determine the size of the data plus the + packed length portion in the row data. + + @returns The length in the row plus the size of the data. + */ + uint32 get_packed_size(const uchar *ptr_arg, bool low_byte_first) + {return packlength + get_length(ptr_arg, low_byte_first);} + + inline uint32 get_length(uint row_offset= 0) + { return get_length(ptr+row_offset, table->s->db_low_byte_first); } + uint32 get_length(const uchar *ptr, bool low_byte_first); + uint32 get_length(const uchar *ptr_arg) + { return get_length(ptr_arg, table->s->db_low_byte_first); } void put_length(uchar *pos, uint32 length); inline void get_ptr(uchar **str) { @@ -1364,7 +1463,7 @@ public: { uchar *tmp; get_ptr(&tmp); - if (value.copy((char*) tmp, get_length(),charset())) + if (value.copy((char*) tmp, get_length(), charset())) { Field_blob::reset(); return 1; @@ -1377,6 +1476,7 @@ public: uchar *pack_key(uchar *to, const uchar *from, uint max_length); uchar *pack_key_from_key_image(uchar* to, const uchar *from, uint max_length); + virtual const uchar *unpack(uchar *to, const uchar *from, uint param_data); const uchar *unpack(uchar *to, const uchar *from); const uchar *unpack_key(uchar* to, const uchar *from, uint max_length); int pack_cmp(const uchar *a, const uchar *b, uint key_length, @@ -1392,6 +1492,10 @@ public: { return charset() == &my_charset_bin ? FALSE : TRUE; } uint32 max_display_length(); uint is_equal(Create_field *new_field); + inline bool in_read_set() { return bitmap_is_set(table->read_set, field_index); } + inline bool in_write_set() { return bitmap_is_set(table->write_set, field_index); } +private: + int do_save_field_metadata(uchar *first_byte); }; @@ -1461,12 +1565,17 @@ public: void sql_type(String &str) const; uint size_of() const { return sizeof(*this); } enum_field_types real_type() const { return MYSQL_TYPE_ENUM; } + uint pack_length_from_metadata(uint field_metadata) + { return (field_metadata & 0x00ff); } + uint row_pack_length() { return pack_length(); } virtual bool zero_pack() const { return 0; } bool optimize_range(uint idx, uint part) { return 0; } bool eq_def(Field *field); bool has_charset(void) const { return TRUE; } /* enum and set are sorted as integers */ CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; } +private: + int do_save_field_metadata(uchar *first_byte); }; @@ -1487,6 +1596,7 @@ public: int store(const char *to,uint length,CHARSET_INFO *charset); int store(double nr) { return Field_set::store((longlong) nr, FALSE); } int store(longlong nr, bool unsigned_val); + virtual bool zero_pack() const { return 1; } String *val_str(String*,String *); void sql_type(String &str) const; @@ -1535,7 +1645,10 @@ public: virtual bool str_needs_quotes() { return TRUE; } my_decimal *val_decimal(my_decimal *); int cmp(const uchar *a, const uchar *b) - { return cmp_binary(a, b); } + { + DBUG_ASSERT(ptr == a); + return Field_bit::key_cmp(b, bytes_in_rec+test(bit_len)); + } int cmp_binary_offset(uint row_offset) { return cmp_offset(row_offset); } int cmp_max(const uchar *a, const uchar *b, uint max_length); @@ -1543,6 +1656,10 @@ public: { return cmp_binary((uchar *) a, (uchar *) b); } int key_cmp(const uchar *str, uint length); int cmp_offset(uint row_offset); + void get_image(uchar *buff, uint length, CHARSET_INFO *cs) + { get_key_image(buff, length, itRAW); } + void set_image(const uchar *buff,uint length, CHARSET_INFO *cs) + { Field_bit::store((char *) buff, length, cs); } uint get_key_image(uchar *buff, uint length, imagetype type); void set_key_image(const uchar *buff, uint length) { Field_bit::store((char*) buff, length, &my_charset_bin); } @@ -1550,8 +1667,13 @@ public: { get_key_image(buff, length, itRAW); } uint32 pack_length() const { return (uint32) (field_length + 7) / 8; } uint32 pack_length_in_rec() const { return bytes_in_rec; } + uint pack_length_from_metadata(uint field_metadata); + uint row_pack_length() + { return (bytes_in_rec + ((bit_len > 0) ? 1 : 0)); } + int compatible_field_size(uint field_metadata); void sql_type(String &str) const; uchar *pack(uchar *to, const uchar *from, uint max_length=~(uint) 0); + virtual const uchar *unpack(uchar *to, const uchar *from, uint param_data); const uchar *unpack(uchar* to, const uchar *from); virtual void set_default(); @@ -1579,6 +1701,7 @@ public: private: virtual size_t do_last_null_byte() const; + int do_save_field_metadata(uchar *first_byte); }; @@ -1680,7 +1803,12 @@ class Send_field { */ class Copy_field :public Sql_alloc { - void (*get_copy_func(Field *to,Field *from))(Copy_field *); + /** + Convenience definition of a copy function returned by + get_copy_func. + */ + typedef void Copy_func(Copy_field*); + Copy_func *get_copy_func(Field *to, Field *from); public: uchar *from_ptr,*to_ptr; uchar *from_null_ptr,*to_null_ptr; diff --git a/sql/field_conv.cc b/sql/field_conv.cc index 44aea9acee0..16e27bb6cab 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -620,7 +620,8 @@ void Copy_field::set(Field *to,Field *from,bool save) } -void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*) +Copy_field::Copy_func * +Copy_field::get_copy_func(Field *to,Field *from) { bool compatible_db_low_byte_first= (to->table->s->db_low_byte_first == from->table->s->db_low_byte_first); @@ -795,11 +796,18 @@ int field_conv(Field *to,Field *from) blob->value.copy(); 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() != MYSQL_TYPE_ENUM && - from->real_type() != MYSQL_TYPE_SET))) || - to->type() == MYSQL_TYPE_DECIMAL) + if (from->real_type() == MYSQL_TYPE_ENUM && + to->real_type() == MYSQL_TYPE_ENUM && + from->val_int() == 0) + { + ((Field_enum *)(to))->store_type(0); + return 0; + } + else if ((from->result_type() == STRING_RESULT && + (to->result_type() == STRING_RESULT || + (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()); diff --git a/sql/filesort.cc b/sql/filesort.cc index 0012bebf688..a8e8431267c 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -1120,7 +1120,8 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, int flag) { int error; - uint rec_length,sort_length,res_length,offset; + uint rec_length,res_length,offset; + size_t sort_length; ulong maxcount; ha_rows max_rows,org_max_rows; my_off_t to_start_filepos; @@ -1128,6 +1129,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, BUFFPEK *buffpek; QUEUE queue; qsort2_cmp cmp; + void *first_cmp_arg; volatile THD::killed_state *killed= ¤t_thd->killed; THD::killed_state not_killable; DBUG_ENTER("merge_buffers"); @@ -1152,9 +1154,18 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, /* The following will fire if there is not enough space in sort_buffer */ DBUG_ASSERT(maxcount!=0); + if (param->unique_buff) + { + cmp= param->compare; + first_cmp_arg= (void *) ¶m->cmp_context; + } + else + { + cmp= get_ptr_compare(sort_length); + first_cmp_arg= (void*) &sort_length; + } if (init_queue(&queue, (uint) (Tb-Fb)+1, offsetof(BUFFPEK,key), 0, - (queue_compare) (cmp= get_ptr_compare(sort_length)), - (void*) &sort_length)) + (queue_compare) cmp, first_cmp_arg)) DBUG_RETURN(1); /* purecov: inspected */ for (buffpek= Fb ; buffpek <= Tb ; buffpek++) { @@ -1207,7 +1218,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, buffpek= (BUFFPEK*) queue_top(&queue); if (cmp) // Remove duplicates { - if (!(*cmp)(&sort_length, &(param->unique_buff), + if (!(*cmp)(first_cmp_arg, &(param->unique_buff), (uchar**) &buffpek->key)) goto skip_duplicate; memcpy(param->unique_buff, (uchar*) buffpek->key, rec_length); @@ -1259,7 +1270,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, */ if (cmp) { - if (!(*cmp)(&sort_length, &(param->unique_buff), (uchar**) &buffpek->key)) + if (!(*cmp)(first_cmp_arg, &(param->unique_buff), (uchar**) &buffpek->key)) { buffpek->key+= rec_length; // Remove duplicate --buffpek->mem_count; diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index c78cb4e65b4..b2152fbb906 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -229,14 +229,12 @@ static int ndb_to_mysql_error(const NdbError *ndberr) */ case HA_ERR_NO_SUCH_TABLE: case HA_ERR_KEY_NOT_FOUND: - case HA_ERR_FOUND_DUPP_KEY: return error; /* Mapping missing, go with the ndb error code*/ case -1: error= ndberr->code; break; - /* Mapping exists, go with the mapped code */ default: break; @@ -331,6 +329,7 @@ Thd_ndb::Thd_ndb() all= NULL; stmt= NULL; m_error= FALSE; + m_error_code= 0; query_state&= NDB_QUERY_NORMAL; options= 0; (void) hash_init(&open_tables, &my_charset_bin, 5, 0, 0, @@ -366,6 +365,7 @@ Thd_ndb::init_open_tables() { count= 0; m_error= FALSE; + m_error_code= 0; my_hash_reset(&open_tables); } @@ -489,6 +489,7 @@ void ha_ndbcluster::no_uncommitted_rows_execute_failure() return; DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_execute_failure"); get_thd_ndb(current_thd)->m_error= TRUE; + get_thd_ndb(current_thd)->m_error_code= 0; DBUG_VOID_RETURN; } @@ -2086,9 +2087,15 @@ int ha_ndbcluster::unique_index_read(const uchar *key, if (execute_no_commit_ie(this,trans,FALSE) != 0 || op->getNdbError().code) { - table->status= STATUS_NOT_FOUND; - DBUG_RETURN(ndb_err(trans)); + int err= ndb_err(trans); + if(err==HA_ERR_KEY_NOT_FOUND) + table->status= STATUS_NOT_FOUND; + else + table->status= STATUS_GARBAGE; + + DBUG_RETURN(err); } + // The value have now been fetched from NDB unpack_record(buf); table->status= 0; @@ -2812,11 +2819,17 @@ int ha_ndbcluster::write_row(uchar *record) if (unlikely(m_slow_path)) { + /* + ignore TNTO_NO_LOGGING for slave thd. It is used to indicate + log-slave-updates option. This is instead handled in the + injector thread, by looking explicitly at the + opt_log_slave_updates flag. + */ Thd_ndb *thd_ndb= get_thd_ndb(thd); - if (thd_ndb->trans_options & TNTO_NO_LOGGING) - op->setAnyValue(NDB_ANYVALUE_FOR_NOLOGGING); - else if (thd->slave_thread) + if (thd->slave_thread) op->setAnyValue(thd->server_id); + else if (thd_ndb->trans_options & TNTO_NO_LOGGING) + op->setAnyValue(NDB_ANYVALUE_FOR_NOLOGGING); } m_rows_changed++; @@ -3102,11 +3115,17 @@ int ha_ndbcluster::update_row(const uchar *old_data, uchar *new_data) if (unlikely(m_slow_path)) { + /* + ignore TNTO_NO_LOGGING for slave thd. It is used to indicate + log-slave-updates option. This is instead handled in the + injector thread, by looking explicitly at the + opt_log_slave_updates flag. + */ Thd_ndb *thd_ndb= get_thd_ndb(thd); - if (thd_ndb->trans_options & TNTO_NO_LOGGING) - op->setAnyValue(NDB_ANYVALUE_FOR_NOLOGGING); - else if (thd->slave_thread) + if (thd->slave_thread) op->setAnyValue(thd->server_id); + else if (thd_ndb->trans_options & TNTO_NO_LOGGING) + op->setAnyValue(NDB_ANYVALUE_FOR_NOLOGGING); } /* Execute update operation if we are not doing a scan for update @@ -3170,13 +3189,19 @@ int ha_ndbcluster::delete_row(const uchar *record) if (unlikely(m_slow_path)) { + /* + ignore TNTO_NO_LOGGING for slave thd. It is used to indicate + log-slave-updates option. This is instead handled in the + injector thread, by looking explicitly at the + opt_log_slave_updates flag. + */ Thd_ndb *thd_ndb= get_thd_ndb(thd); - if (thd_ndb->trans_options & TNTO_NO_LOGGING) - ((NdbOperation *)trans->getLastDefinedOperation())-> - setAnyValue(NDB_ANYVALUE_FOR_NOLOGGING); - else if (thd->slave_thread) + if (thd->slave_thread) ((NdbOperation *)trans->getLastDefinedOperation())-> setAnyValue(thd->server_id); + else if (thd_ndb->trans_options & TNTO_NO_LOGGING) + ((NdbOperation *)trans->getLastDefinedOperation())-> + setAnyValue(NDB_ANYVALUE_FOR_NOLOGGING); } if (!(m_primary_key_update || m_delete_cannot_batch)) // If deleting from cursor, NoCommit will be handled in next_result @@ -3210,11 +3235,17 @@ int ha_ndbcluster::delete_row(const uchar *record) if (unlikely(m_slow_path)) { + /* + ignore TNTO_NO_LOGGING for slave thd. It is used to indicate + log-slave-updates option. This is instead handled in the + injector thread, by looking explicitly at the + opt_log_slave_updates flag. + */ Thd_ndb *thd_ndb= get_thd_ndb(thd); - if (thd_ndb->trans_options & TNTO_NO_LOGGING) - op->setAnyValue(NDB_ANYVALUE_FOR_NOLOGGING); - else if (thd->slave_thread) + if (thd->slave_thread) op->setAnyValue(thd->server_id); + else if (thd_ndb->trans_options & TNTO_NO_LOGGING) + op->setAnyValue(NDB_ANYVALUE_FOR_NOLOGGING); } } @@ -3960,6 +3991,8 @@ int ha_ndbcluster::info(uint flag) DBUG_PRINT("info", ("HA_STATUS_AUTO")); if (m_table && table->found_next_number_field) { + if ((my_errno= check_ndb_connection())) + DBUG_RETURN(my_errno); Ndb *ndb= get_ndb(); Ndb_tuple_id_range_guard g(m_share); @@ -4287,7 +4320,7 @@ THR_LOCK_DATA **ha_ndbcluster::store_lock(THD *thd, */ #ifdef HAVE_NDB_BINLOG -extern MASTER_INFO *active_mi; +extern Master_info *active_mi; static int ndbcluster_update_apply_status(THD *thd, int do_update) { Thd_ndb *thd_ndb= get_thd_ndb(thd); @@ -4366,7 +4399,7 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) { m_transaction_on= FALSE; /* Would be simpler if has_transactions() didn't always say "yes" */ - thd->no_trans_update.all= thd->no_trans_update.stmt= TRUE; + thd->transaction.all.modified_non_trans_table= thd->transaction.stmt.modified_non_trans_table= TRUE; } else if (!thd->transaction.on) m_transaction_on= FALSE; @@ -4383,7 +4416,10 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) trans= ndb->startTransaction(); if (trans == NULL) + { + thd_ndb->lock_count= 0; ERR_RETURN(ndb->getNdbError()); + } thd_ndb->init_open_tables(); thd_ndb->stmt= trans; thd_ndb->query_state&= NDB_QUERY_NORMAL; @@ -4409,7 +4445,10 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) trans= ndb->startTransaction(); if (trans == NULL) + { + thd_ndb->lock_count= 0; ERR_RETURN(ndb->getNdbError()); + } thd_ndb->init_open_tables(); thd_ndb->all= trans; thd_ndb->query_state&= NDB_QUERY_NORMAL; @@ -6877,7 +6916,7 @@ int ndbcluster_find_all_files(THD *thd) int ndbcluster_find_files(handlerton *hton, THD *thd, const char *db, const char *path, - const char *wild, bool dir, List<char> *files) + const char *wild, bool dir, List<LEX_STRING> *files) { DBUG_ENTER("ndbcluster_find_files"); DBUG_PRINT("enter", ("db: %s", db)); @@ -6944,21 +6983,39 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, my_hash_insert(&ndb_tables, (uchar*)thd->strdup(elmt.name)); } - char *file_name; - List_iterator<char> it(*files); + LEX_STRING *file_name; + List_iterator<LEX_STRING> it(*files); List<char> delete_list; + char *file_name_str; while ((file_name=it++)) { bool file_on_disk= FALSE; - DBUG_PRINT("info", ("%s", file_name)); - if (hash_search(&ndb_tables, (uchar*) file_name, strlen(file_name))) + DBUG_PRINT("info", ("%s", file_name->str)); + if (hash_search(&ndb_tables, (uchar*) file_name->str, file_name->length)) { - DBUG_PRINT("info", ("%s existed in NDB _and_ on disk ", file_name)); + build_table_filename(name, sizeof(name), db, file_name->str, reg_ext, 0); + if (my_access(name, F_OK)) + { + pthread_mutex_lock(&LOCK_open); + DBUG_PRINT("info", ("Table %s listed and need discovery", + file_name->str)); + if (ndb_create_table_from_engine(thd, db, file_name->str)) + { + pthread_mutex_unlock(&LOCK_open); + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TABLE_EXISTS_ERROR, + "Discover of table %s.%s failed", + db, file_name->str); + continue; + } + pthread_mutex_unlock(&LOCK_open); + } + DBUG_PRINT("info", ("%s existed in NDB _and_ on disk ", file_name->str)); file_on_disk= TRUE; } // Check for .ndb file with this name - build_table_filename(name, sizeof(name), db, file_name, ha_ndb_ext, 0); + build_table_filename(name, sizeof(name), db, file_name->str, ha_ndb_ext, 0); DBUG_PRINT("info", ("Check access for %s", name)); if (my_access(name, F_OK)) { @@ -6966,33 +7023,34 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, // .ndb file did not exist on disk, another table type if (file_on_disk) { - // Ignore this ndb table - uchar *record= hash_search(&ndb_tables, (uchar*) file_name, - strlen(file_name)); + // Ignore this ndb table + uchar *record= hash_search(&ndb_tables, (uchar*) file_name->str, + file_name->length); DBUG_ASSERT(record); hash_delete(&ndb_tables, record); push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_TABLE_EXISTS_ERROR, "Local table %s.%s shadows ndb table", - db, file_name); + db, file_name->str); } continue; } if (file_on_disk) { // File existed in NDB and as frm file, put in ok_tables list - my_hash_insert(&ok_tables, (uchar*)file_name); + my_hash_insert(&ok_tables, (uchar*) file_name->str); continue; } DBUG_PRINT("info", ("%s existed on disk", name)); // The .ndb file exists on disk, but it's not in list of tables in ndb // Verify that handler agrees table is gone. - if (ndbcluster_table_exists_in_engine(hton, thd, db, file_name) == HA_ERR_NO_SUCH_TABLE) + if (ndbcluster_table_exists_in_engine(hton, thd, db, file_name->str) == + HA_ERR_NO_SUCH_TABLE) { - DBUG_PRINT("info", ("NDB says %s does not exists", file_name)); + DBUG_PRINT("info", ("NDB says %s does not exists", file_name->str)); it.remove(); // Put in list of tables to remove from disk - delete_list.push_back(thd->strdup(file_name)); + delete_list.push_back(thd->strdup(file_name->str)); } } @@ -7003,12 +7061,12 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, build_table_filename(name, sizeof(name), db, "", "", 0); for (i= 0; i < ok_tables.records; i++) { - file_name= (char*)hash_element(&ok_tables, i); + file_name_str= (char*)hash_element(&ok_tables, i); end= end1 + - tablename_to_filename(file_name, end1, sizeof(name) - (end1 - name)); + tablename_to_filename(file_name_str, end1, sizeof(name) - (end1 - name)); pthread_mutex_lock(&LOCK_open); ndbcluster_create_binlog_setup(ndb, name, end-name, - db, file_name, TRUE); + db, file_name_str, TRUE); pthread_mutex_unlock(&LOCK_open); } } @@ -7019,56 +7077,60 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, List<char> create_list; for (i= 0 ; i < ndb_tables.records ; i++) { - file_name= (char*) hash_element(&ndb_tables, i); - if (!hash_search(&ok_tables, (uchar*) file_name, strlen(file_name))) + file_name_str= (char*) hash_element(&ndb_tables, i); + if (!hash_search(&ok_tables, (uchar*) file_name_str, strlen(file_name_str))) { - build_table_filename(name, sizeof(name), db, file_name, reg_ext, 0); + build_table_filename(name, sizeof(name), db, file_name_str, reg_ext, 0); if (my_access(name, F_OK)) { - DBUG_PRINT("info", ("%s must be discovered", file_name)); + DBUG_PRINT("info", ("%s must be discovered", file_name_str)); // File is in list of ndb tables and not in ok_tables // This table need to be created - create_list.push_back(thd->strdup(file_name)); + create_list.push_back(thd->strdup(file_name_str)); } } } - // Lock mutex before deleting and creating frm files - pthread_mutex_lock(&LOCK_open); - if (!global_read_lock) { // Delete old files List_iterator_fast<char> it3(delete_list); - while ((file_name=it3++)) + while ((file_name_str= it3++)) { - DBUG_PRINT("info", ("Remove table %s/%s", db, file_name)); + DBUG_PRINT("info", ("Remove table %s/%s", db, file_name_str)); // Delete the table and all related files TABLE_LIST table_list; bzero((char*) &table_list,sizeof(table_list)); table_list.db= (char*) db; - table_list.alias= table_list.table_name= (char*)file_name; + table_list.alias= table_list.table_name= (char*)file_name_str; (void)mysql_rm_table_part2(thd, &table_list, - /* if_exists */ FALSE, - /* drop_temporary */ FALSE, - /* drop_view */ FALSE, - /* dont_log_query*/ TRUE); + FALSE, /* if_exists */ + FALSE, /* drop_temporary */ + FALSE, /* drop_view */ + TRUE /* dont_log_query*/); + /* Clear error message that is returned when table is deleted */ thd->clear_error(); } } + pthread_mutex_lock(&LOCK_open); // Create new files List_iterator_fast<char> it2(create_list); - while ((file_name=it2++)) + while ((file_name_str=it2++)) { - DBUG_PRINT("info", ("Table %s need discovery", file_name)); - if (ndb_create_table_from_engine(thd, db, file_name) == 0) - files->push_back(thd->strdup(file_name)); + DBUG_PRINT("info", ("Table %s need discovery", file_name_str)); + if (ndb_create_table_from_engine(thd, db, file_name_str) == 0) + { + LEX_STRING *tmp_file_name= 0; + tmp_file_name= thd->make_lex_string(tmp_file_name, file_name_str, + strlen(file_name_str), TRUE); + files->push_back(tmp_file_name); + } } pthread_mutex_unlock(&LOCK_open); - + hash_free(&ok_tables); hash_free(&ndb_tables); @@ -7078,8 +7140,8 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, uint count = 0; while (count++ < files->elements) { - file_name = (char *)files->pop(); - if (!strcmp(file_name, NDB_SCHEMA_TABLE)) + file_name = (LEX_STRING *)files->pop(); + if (!strcmp(file_name->str, NDB_SCHEMA_TABLE)) { DBUG_PRINT("info", ("skip %s.%s table, it should be hidden to user", NDB_REP_DB, NDB_SCHEMA_TABLE)); @@ -8015,9 +8077,9 @@ int handle_trailing_share(NDB_SHARE *share) } } - sql_print_error("NDB_SHARE: %s already exists use_count=%d." - " Moving away for safety, but possible memleak.", - share->key, share->use_count); + sql_print_warning("NDB_SHARE: %s already exists use_count=%d." + " Moving away for safety, but possible memleak.", + share->key, share->use_count); dbug_print_open_tables(); /* @@ -8213,7 +8275,15 @@ NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table, share->table_name= share->db + strlen(share->db) + 1; ha_ndbcluster::set_tabname(key, share->table_name); #ifdef HAVE_NDB_BINLOG - ndbcluster_binlog_init_share(share, table); + if (ndbcluster_binlog_init_share(share, table)) + { + DBUG_PRINT("error", ("get_share: %s could not init share", key)); + ndbcluster_real_free_share(&share); + *root_ptr= old_root; + if (!have_lock) + pthread_mutex_unlock(&ndbcluster_mutex); + DBUG_RETURN(0); + } #endif *root_ptr= old_root; } @@ -8524,7 +8594,8 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, 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))) + null_value_index_search(ranges, ranges+range_count, buffer)) + || m_delete_cannot_batch || m_update_cannot_batch) { m_disable_multi_read= TRUE; DBUG_RETURN(handler::read_multi_range_first(found_range_p, @@ -10141,6 +10212,10 @@ static int ndbcluster_fill_files_table(handlerton *hton, { if (ndberr.classification == NdbError::SchemaError) continue; + + if (ndberr.classification == NdbError::UnknownResultError) + continue; + ERR_RETURN(ndberr); } NdbDictionary::Tablespace ts= dict->getTablespace(df.getTablespace()); @@ -10220,6 +10295,8 @@ static int ndbcluster_fill_files_table(handlerton *hton, { if (ndberr.classification == NdbError::SchemaError) continue; + if (ndberr.classification == NdbError::UnknownResultError) + continue; ERR_RETURN(ndberr); } NdbDictionary::LogfileGroup lfg= diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index 4f5a1359d2d..a6f992226c2 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -563,7 +563,7 @@ extern SHOW_VAR ndb_status_variables[]; int ndbcluster_discover(THD* thd, const char* dbname, const char* name, const void** frmblob, uint* frmlen); int ndbcluster_find_files(THD *thd,const char *db,const char *path, - const char *wild, bool dir, List<char> *files); + const char *wild, bool dir, List<LEX_STRING> *files); int ndbcluster_table_exists_in_engine(THD* thd, const char *db, const char *name); void ndbcluster_print_error(int error, const NdbOperation *error_op); diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index f388b60f3d5..5d5c8a26447 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -114,6 +114,9 @@ NDB_SHARE *ndb_apply_status_share= 0; NDB_SHARE *ndb_schema_share= 0; pthread_mutex_t ndb_schema_share_mutex; +extern my_bool opt_log_slave_updates; +static my_bool g_ndb_log_slave_updates; + /* Schema object distribution handling */ HASH ndb_schema_objects; typedef struct st_ndb_schema_object { @@ -261,12 +264,13 @@ static void run_query(THD *thd, char *buf, char *end, int i; Thd_ndb *thd_ndb= get_thd_ndb(thd); for (i= 0; no_print_error[i]; i++) - if (thd_ndb->m_error == no_print_error[i]) + if ((thd_ndb->m_error_code == no_print_error[i]) || + (thd->net.last_errno == (unsigned)no_print_error[i])) break; if (!no_print_error[i]) sql_print_error("NDB: %s: error %s %d(ndb: %d) %d %d", buf, thd->net.last_error, thd->net.last_errno, - thd_ndb->m_error, + thd_ndb->m_error_code, thd->net.report_error, thd->query_error); } @@ -321,18 +325,14 @@ ndbcluster_binlog_open_table(THD *thd, NDB_SHARE *share, share->key); if ((error= open_table_def(thd, table_share, 0))) { - sql_print_error("Unable to get table share for %s, error=%d", - share->key, error); - DBUG_PRINT("error", ("open_table_def failed %d", error)); + DBUG_PRINT("error", ("open_table_def failed: %d my_errno: %d", error, my_errno)); free_table_share(table_share); DBUG_RETURN(error); } if ((error= open_table_from_share(thd, table_share, "", 0 /* fon't allocate buffers */, (uint) READ_ALL, 0, table, FALSE))) { - sql_print_error("Unable to open table for %s, error=%d(%d)", - share->key, error, my_errno); - DBUG_PRINT("error", ("open_table_from_share failed %d", error)); + DBUG_PRINT("error", ("open_table_from_share failed %d my_errno: %d", error, my_errno)); free_table_share(table_share); DBUG_RETURN(error); } @@ -378,11 +378,12 @@ ndbcluster_binlog_open_table(THD *thd, NDB_SHARE *share, /* Initialize the binlog part of the NDB_SHARE */ -void ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *_table) +int ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *_table) { THD *thd= current_thd; MEM_ROOT *mem_root= &share->mem_root; int do_event_op= ndb_binlog_running; + int error= 0; DBUG_ENTER("ndbcluster_binlog_init_share"); share->connect_count= g_ndb_cluster_connection->get_connect_count(); @@ -425,7 +426,7 @@ void ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *_table) { share->flags|= NSF_NO_BINLOG; } - DBUG_VOID_RETURN; + DBUG_RETURN(error); } while (1) { @@ -452,7 +453,7 @@ void ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *_table) share->flags|= NSF_BLOB_FLAG; break; } - DBUG_VOID_RETURN; + DBUG_RETURN(error); } /***************************************************************** @@ -776,7 +777,10 @@ static int ndbcluster_create_ndb_apply_status_table(THD *thd) " end_pos BIGINT UNSIGNED NOT NULL, " " PRIMARY KEY USING HASH (server_id) ) ENGINE=NDB"); - const int no_print_error[3]= {701, 4009, 0}; // do not print error 701 etc + const int no_print_error[4]= {ER_TABLE_EXISTS_ERROR, + 701, + 4009, + 0}; // do not print error 701 etc run_query(thd, buf, end, no_print_error, TRUE); DBUG_RETURN(0); @@ -833,7 +837,10 @@ static int ndbcluster_create_schema_table(THD *thd) " type INT UNSIGNED NOT NULL," " PRIMARY KEY USING HASH (db,name) ) ENGINE=NDB"); - const int no_print_error[3]= {701, 4009, 0}; // do not print error 701 etc + const int no_print_error[4]= {ER_TABLE_EXISTS_ERROR, + 701, + 4009, + 0}; // do not print error 701 etc run_query(thd, buf, end, no_print_error, TRUE); DBUG_RETURN(0); @@ -2480,8 +2487,8 @@ 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; + LEX_STRING *tabname; + List<LEX_STRING> files; char path[FN_REFLEN]; build_table_filename(path, sizeof(path), dbname, "", "", 0); @@ -2493,8 +2500,8 @@ ndbcluster_check_if_local_tables_in_db(THD *thd, const char *dbname) 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_PRINT("info", ("Found table %s", tabname->str)); + if (ndbcluster_check_if_local_table(dbname, tabname->str)) DBUG_RETURN(true); } @@ -3075,7 +3082,7 @@ ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name, /* ToDo; handle error? */ if (share && share->op && share->op->getState() == NdbEventOperation::EO_EXECUTING && - dict->getNdbError().code != 4009) + dict->getNdbError().mysql_code != HA_ERR_NO_CONNECTION) { DBUG_ASSERT(FALSE); DBUG_RETURN(-1); @@ -3202,11 +3209,6 @@ ndb_binlog_thread_handle_non_data_event(THD *thd, Ndb *ndb, NDB_SHARE *share= (NDB_SHARE *)pOp->getCustomData(); NDBEVENT::TableEvent type= pOp->getEventType(); - /* make sure to flush any pending events as they can be dependent - on one of the tables being changed below - */ - thd->binlog_flush_pending_rows_event(TRUE); - switch (type) { case NDBEVENT::TE_CLUSTER_FAILURE: @@ -3296,6 +3298,14 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, originating_server_id); return 0; } + else if (!g_ndb_log_slave_updates) + { + /* + This event comes from a slave applier since it has an originating + server id set. Since option to log slave updates is not set, skip it. + */ + return 0; + } TABLE *table= share->table; DBUG_ASSERT(trans.good()); @@ -3942,6 +3952,8 @@ restart: ! IS_NDB_BLOB_PREFIX(pOp->getEvent()->getTable()->getName())); DBUG_ASSERT(gci <= ndb_latest_received_binlog_epoch); + /* initialize some variables for this epoch */ + g_ndb_log_slave_updates= opt_log_slave_updates; i_ndb-> setReportThreshEventGCISlip(ndb_report_thresh_binlog_epoch_slip); i_ndb->setReportThreshEventFreeMem(ndb_report_thresh_binlog_mem_usage); diff --git a/sql/ha_ndbcluster_binlog.h b/sql/ha_ndbcluster_binlog.h index b5b8d0d9745..1cad643e5ec 100644 --- a/sql/ha_ndbcluster_binlog.h +++ b/sql/ha_ndbcluster_binlog.h @@ -124,7 +124,7 @@ void ndbcluster_binlog_init_handlerton(); /* Initialize the binlog part of the NDB_SHARE */ -void ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *table); +int 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); @@ -216,10 +216,12 @@ inline void free_share(NDB_SHARE **share, bool have_lock= FALSE) inline Thd_ndb * -get_thd_ndb(THD *thd) { return (Thd_ndb *) thd->ha_data[ndbcluster_hton->slot]; } +get_thd_ndb(THD *thd) +{ return (Thd_ndb *) thd_get_ha_data(thd, ndbcluster_hton); } inline void -set_thd_ndb(THD *thd, Thd_ndb *thd_ndb) { thd->ha_data[ndbcluster_hton->slot]= thd_ndb; } +set_thd_ndb(THD *thd, Thd_ndb *thd_ndb) +{ thd_set_ha_data(thd, ndbcluster_hton, thd_ndb); } Ndb* check_ndb_in_thd(THD* thd); diff --git a/sql/ha_ndbcluster_cond.cc b/sql/ha_ndbcluster_cond.cc index c686e83504c..94342c6d2ad 100644 --- a/sql/ha_ndbcluster_cond.cc +++ b/sql/ha_ndbcluster_cond.cc @@ -46,6 +46,14 @@ void ndb_serialize_cond(const Item *item, void *arg) // Check if we are skipping arguments to a function to be evaluated if (context->skip) { + if (!item) + { + DBUG_PRINT("info", ("Unexpected mismatch of found and expected number of function arguments %u", context->skip)); + sql_print_error("ndb_serialize_cond: Unexpected mismatch of found and " + "expected number of function arguments %u", context->skip); + context->skip= 0; + DBUG_VOID_RETURN; + } DBUG_PRINT("info", ("Skiping argument %d", context->skip)); context->skip--; switch (item->type()) { diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 15d7353c03c..a57f1d24758 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -145,6 +145,8 @@ static uint alter_table_flags(uint flags __attribute__((unused))) HA_FAST_CHANGE_PARTITION); } +const uint ha_partition::NO_CURRENT_PART_ID= 0xFFFFFFFF; + /* Constructor method @@ -1125,7 +1127,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, 0 Success */ -int ha_partition::prepare_new_partition(TABLE *table, +int ha_partition::prepare_new_partition(TABLE *tbl, HA_CREATE_INFO *create_info, handler *file, const char *part_name, partition_element *p_elem) @@ -1134,14 +1136,20 @@ int ha_partition::prepare_new_partition(TABLE *table, bool create_flag= FALSE; DBUG_ENTER("prepare_new_partition"); - if ((error= set_up_table_before_create(table, part_name, create_info, + if ((error= set_up_table_before_create(tbl, part_name, create_info, 0, p_elem))) goto error; - if ((error= file->create(part_name, table, create_info))) + if ((error= file->create(part_name, tbl, create_info))) goto error; create_flag= TRUE; - if ((error= file->ha_open(table, part_name, m_mode, m_open_test_lock))) + if ((error= file->ha_open(tbl, part_name, m_mode, m_open_test_lock))) goto error; + /* + Note: if you plan to add another call that may return failure, + better to do it before external_lock() as cleanup_new_partition() + assumes that external_lock() is last call that may fail here. + Otherwise see description for cleanup_new_partition(). + */ if ((error= file->external_lock(current_thd, m_lock_type))) goto error; @@ -1164,6 +1172,14 @@ error: NONE DESCRIPTION + This function is called immediately after prepare_new_partition() in + case the latter fails. + + In prepare_new_partition() last call that may return failure is + external_lock(). That means if prepare_new_partition() fails, + partition does not have external lock. Thus no need to call + external_lock(F_UNLCK) here. + TODO: We must ensure that in the case that we get an error during the process that we call external_lock with F_UNLCK, close the table and delete the @@ -1182,7 +1198,6 @@ void ha_partition::cleanup_new_partition(uint part_count) m_file= m_added_file; m_added_file= NULL; - external_lock(current_thd, F_UNLCK); /* delete_table also needed, a bit more complex */ close(); @@ -1763,7 +1778,7 @@ partition_element *ha_partition::find_partition_element(uint part_id) 4) Data file name on partition */ -int ha_partition::set_up_table_before_create(TABLE *table, +int ha_partition::set_up_table_before_create(TABLE *tbl, const char *partition_name_with_path, HA_CREATE_INFO *info, uint part_id, @@ -1780,8 +1795,8 @@ int ha_partition::set_up_table_before_create(TABLE *table, if (!part_elem) DBUG_RETURN(1); // Fatal error } - table->s->max_rows= part_elem->part_max_rows; - table->s->min_rows= part_elem->part_min_rows; + tbl->s->max_rows= part_elem->part_max_rows; + tbl->s->min_rows= part_elem->part_min_rows; partition_name= strrchr(partition_name_with_path, FN_LIBCHAR); if ((part_elem->index_file_name && (error= append_file_to_dir(thd, @@ -2663,6 +2678,7 @@ int ha_partition::write_row(uchar * buf) uint32 part_id; int error; longlong func_value; + bool autoincrement_lock= false; #ifdef NOT_NEEDED uchar *rec0= m_rec0; #endif @@ -2678,7 +2694,29 @@ int ha_partition::write_row(uchar * buf) or a new row, then update the auto_increment value in the record. */ if (table->next_number_field && buf == table->record[0]) - update_auto_increment(); + { + /* + Some engines (InnoDB for example) can change autoincrement + counter only after 'table->write_row' operation. + So if another thread gets inside the ha_partition::write_row + before it is complete, it gets same auto_increment value, + which means DUP_KEY error (bug #27405) + Here we separate the access using table_share->mutex, and + use autoincrement_lock variable to avoid unnecessary locks. + Probably not an ideal solution. + */ + autoincrement_lock= true; + pthread_mutex_lock(&table_share->mutex); + error= update_auto_increment(); + + /* + If we have failed to set the auto-increment value for this row, + it is highly likely that we will not be able to insert it into + the correct partition. We must check and fail if neccessary. + */ + if (error) + DBUG_RETURN(error); + } my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); #ifdef NOT_NEEDED @@ -2699,11 +2737,15 @@ int ha_partition::write_row(uchar * buf) if (unlikely(error)) { m_part_info->err_value= func_value; - DBUG_RETURN(error); + goto exit; } m_last_part= part_id; DBUG_PRINT("info", ("Insert in partition %d", part_id)); - DBUG_RETURN(m_file[part_id]->write_row(buf)); + error= m_file[part_id]->write_row(buf); +exit: + if (autoincrement_lock) + pthread_mutex_unlock(&table_share->mutex); + DBUG_RETURN(error); } @@ -3220,6 +3262,14 @@ void ha_partition::position(const uchar *record) DBUG_VOID_RETURN; } + +void ha_partition::column_bitmaps_signal() +{ + handler::column_bitmaps_signal(); + bitmap_union(table->read_set, &m_part_info->full_part_field_set); +} + + /* Read row using position @@ -3255,6 +3305,36 @@ int ha_partition::rnd_pos(uchar * buf, uchar *pos) } +/* + Read row using position using given record to find + + SYNOPSIS + rnd_pos_by_record() + record Current record in MySQL Row Format + + RETURN VALUE + >0 Error code + 0 Success + + DESCRIPTION + this works as position()+rnd_pos() functions, but does some extra work, + calculating m_last_part - the partition to where the 'record' + should go. + + called from replication (log_event.cc) +*/ + +int ha_partition::rnd_pos_by_record(uchar *record) +{ + DBUG_ENTER("ha_partition::rnd_pos_by_record"); + + if (unlikely(get_part_for_delete(record, m_rec0, m_part_info, &m_last_part))) + DBUG_RETURN(1); + + DBUG_RETURN(handler::rnd_pos_by_record(record)); +} + + /**************************************************************************** MODULE index scan ****************************************************************************/ @@ -3387,11 +3467,11 @@ int ha_partition::index_end() used in conjuntion with multi read ranges. */ -int ha_partition::index_read(uchar * buf, const uchar * key, - key_part_map keypart_map, - enum ha_rkey_function find_flag) +int ha_partition::index_read_map(uchar *buf, const uchar *key, + key_part_map keypart_map, + enum ha_rkey_function find_flag) { - DBUG_ENTER("ha_partition::index_read"); + DBUG_ENTER("ha_partition::index_read_map"); end_range= 0; m_index_scan_type= partition_index_read; @@ -3553,7 +3633,7 @@ int ha_partition::common_first_last(uchar *buf) index_read_last() buf Read row in MySQL Row Format key Key - keylen Length of key + keypart_map Which part of key is used RETURN VALUE >0 Error code @@ -3564,8 +3644,8 @@ int ha_partition::common_first_last(uchar *buf) Can only be used on indexes supporting HA_READ_ORDER */ -int ha_partition::index_read_last(uchar *buf, const uchar *key, - key_part_map keypart_map) +int ha_partition::index_read_last_map(uchar *buf, const uchar *key, + key_part_map keypart_map) { DBUG_ENTER("ha_partition::index_read_last"); @@ -3916,9 +3996,9 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf) switch (m_index_scan_type) { case partition_index_read: DBUG_PRINT("info", ("index_read on partition %d", i)); - error= file->index_read(buf, m_start_key.key, - m_start_key.keypart_map, - m_start_key.flag); + error= file->index_read_map(buf, m_start_key.key, + m_start_key.keypart_map, + m_start_key.flag); break; case partition_index_first: DBUG_PRINT("info", ("index_first on partition %d", i)); @@ -4007,10 +4087,10 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) switch (m_index_scan_type) { case partition_index_read: - error= file->index_read(rec_buf_ptr, - m_start_key.key, - m_start_key.keypart_map, - m_start_key.flag); + error= file->index_read_map(rec_buf_ptr, + m_start_key.key, + m_start_key.keypart_map, + m_start_key.flag); break; case partition_index_first: error= file->index_first(rec_buf_ptr); @@ -4021,9 +4101,9 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) reverse_order= TRUE; break; case partition_index_read_last: - error= file->index_read_last(rec_buf_ptr, - m_start_key.key, - m_start_key.keypart_map); + error= file->index_read_last_map(rec_buf_ptr, + m_start_key.key, + m_start_key.keypart_map); reverse_order= TRUE; break; default: @@ -5432,16 +5512,20 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment, ulonglong first_value_part, last_value_part, nb_reserved_values_part, last_value= ~ (ulonglong) 0; handler **pos, **end; + bool retry= TRUE; DBUG_ENTER("ha_partition::get_auto_increment"); +again: for (pos=m_file, end= m_file+ m_tot_parts; pos != end ; pos++) { + first_value_part= *first_value; (*pos)->get_auto_increment(offset, increment, nb_desired_values, &first_value_part, &nb_reserved_values_part); if (first_value_part == ~(ulonglong)(0)) // error in one partition { *first_value= first_value_part; - break; + sql_print_error("Partition failed to reserve auto_increment value"); + DBUG_VOID_RETURN; } /* Partition has reserved an interval. Intersect it with the intervals @@ -5454,6 +5538,25 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment, } if (last_value < *first_value) /* empty intersection, error */ { + /* + When we have an empty intersection, it means that one or more + partitions may have a significantly different autoinc next value. + We should not fail here - it just means that we should try to + find a new reservation making use of the current *first_value + wbich should now be compatible with all partitions. + */ + if (retry) + { + retry= FALSE; + last_value= ~ (ulonglong) 0; + release_auto_increment(); + goto again; + } + /* + We should not get here. + */ + sql_print_error("Failed to calculate auto_increment value for partition"); + *first_value= ~(ulonglong)(0); } if (increment) // If not check for values diff --git a/sql/ha_partition.h b/sql/ha_partition.h index bc4747be85b..ac00581fae0 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -353,6 +353,7 @@ public: virtual int rnd_end(); virtual int rnd_next(uchar * buf); virtual int rnd_pos(uchar * buf, uchar * pos); + virtual int rnd_pos_by_record(uchar *record); virtual void position(const uchar * record); /* @@ -387,9 +388,9 @@ public: index_init initializes an index before using it and index_end does any end processing needed. */ - virtual int index_read(uchar * buf, const uchar * key, - key_part_map keypart_map, - enum ha_rkey_function find_flag); + virtual int index_read_map(uchar * buf, const uchar * key, + key_part_map keypart_map, + enum ha_rkey_function find_flag); virtual int index_init(uint idx, bool sorted); virtual int index_end(); @@ -402,8 +403,8 @@ public: virtual int index_first(uchar * buf); virtual int index_last(uchar * buf); virtual int index_next_same(uchar * buf, const uchar * key, uint keylen); - virtual int index_read_last(uchar * buf, const uchar * key, - key_part_map keypart_map); + virtual int index_read_last_map(uchar * buf, const uchar * key, + key_part_map keypart_map); /* read_first_row is virtual method but is only implemented by @@ -449,6 +450,7 @@ private: int handle_ordered_next(uchar * buf, bool next_same); int handle_ordered_prev(uchar * buf); void return_top_record(uchar * buf); + void column_bitmaps_signal(); public: /* ------------------------------------------------------------------------- @@ -467,7 +469,7 @@ public: virtual int reset(void); private: - static const uint NO_CURRENT_PART_ID= 0xFFFFFFFF; + static const uint NO_CURRENT_PART_ID; int loop_extra(enum ha_extra_function operation); void late_extra_cache(uint partition_id); void late_extra_no_cache(uint partition_id); diff --git a/sql/handler.cc b/sql/handler.cc index 7d03599fe8b..2839b8f6e6f 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -25,8 +25,6 @@ #include "mysql_priv.h" #include "rpl_filter.h" - - #include <myisampack.h> #include <errno.h> @@ -68,7 +66,7 @@ static const LEX_STRING sys_table_aliases[]= }; const char *ha_row_type[] = { - "", "FIXED", "DYNAMIC", "COMPRESSED", "REDUNDANT", "COMPACT", "PAGE","?","?","?" + "", "FIXED", "DYNAMIC", "COMPRESSED", "REDUNDANT", "COMPACT", "PAGE", "?","?","?" }; const char *tx_isolation_names[] = @@ -565,7 +563,7 @@ static my_bool closecon_handlerton(THD *thd, plugin_ref plugin, be rolled back already */ if (hton->state == SHOW_OPTION_YES && hton->close_connection && - thd->ha_data[hton->slot]) + thd_get_ha_data(thd, hton)) hton->close_connection(hton, thd); return FALSE; } @@ -853,6 +851,9 @@ int ha_rollback_trans(THD *thd, bool all) } } #endif /* USING_TRANSACTIONS */ + if (all) + thd->transaction_rollback_request= FALSE; + /* If a non-transactional table was updated, warn; don't warn if this is a slave thread (because when a slave thread executes a ROLLBACK, it has @@ -862,7 +863,7 @@ int ha_rollback_trans(THD *thd, bool all) the error log; but we don't want users to wonder why they have this message in the error log, so we don't send it. */ - if (is_real_trans && thd->no_trans_update.all && + if (is_real_trans && thd->transaction.all.modified_non_trans_table && !thd->slave_thread && thd->killed != THD::KILL_CONNECTION) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARNING_NOT_COMPLETE_ROLLBACK, @@ -889,6 +890,8 @@ int ha_autocommit_or_rollback(THD *thd, int error) if (ha_commit_stmt(thd)) error=1; } + else if (thd->transaction_rollback_request && !thd->in_sub_stmt) + (void) ha_rollback(thd); else (void) ha_rollback_stmt(thd); @@ -1508,7 +1511,7 @@ void handler::ha_statistic_increment(ulong SSV::*offset) const void **handler::ha_data(THD *thd) const { - return (void **) thd->ha_data + ht->slot; + return thd_ha_data(thd, ht); } THD *handler::ha_thd(void) const @@ -1518,34 +1521,6 @@ THD *handler::ha_thd(void) const } -bool handler::check_if_log_table_locking_is_allowed(uint sql_command, - ulong type, TABLE *table) -{ - /* - Deny locking of the log tables, which is incompatible with - 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 && - sql_command != SQLCOM_ALTER_TABLE && - !(sql_command == SQLCOM_FLUSH && - type & REFRESH_LOG) && - (table->reginfo.lock_type >= TL_READ_NO_INSERT)) - { - /* - The check >= TL_READ_NO_INSERT denies all write locks - plus the only read lock (TL_READ_NO_INSERT itself) - */ - table->reginfo.lock_type == TL_READ_NO_INSERT ? - my_error(ER_CANT_READ_LOCK_LOG_TABLE, MYF(0)) : - my_error(ER_CANT_WRITE_LOCK_LOG_TABLE, MYF(0)); - return FALSE; - } - return TRUE; -} - /** @brief Open database-handler. @@ -2018,9 +1993,9 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment, key_copy(key, table->record[0], table->key_info + table->s->next_number_index, table->s->next_number_key_offset); - error= index_read(table->record[1], key, - make_prev_keypart_map(table->s->next_number_keypart), - HA_READ_PREFIX_LAST); + error= index_read_map(table->record[1], key, + make_prev_keypart_map(table->s->next_number_keypart), + HA_READ_PREFIX_LAST); /* MySQL needs to call us for next row: assume we are inserting ("a",null) here, we return 3, and next this statement will want to insert @@ -2357,7 +2332,13 @@ static bool update_frm_version(TABLE *table) int result= 1; DBUG_ENTER("update_frm_version"); - if (table->s->mysql_version != MYSQL_VERSION_ID) + /* + No need to update frm version in case table was created or checked + by server with the same version. This also ensures that we do not + update frm version for temporary tables as this code doesn't support + temporary tables. + */ + if (table->s->mysql_version == MYSQL_VERSION_ID) DBUG_RETURN(0); strxmov(path, table->s->normalized_path.str, reg_ext, NullS); @@ -2945,7 +2926,7 @@ struct st_find_files_args const char *path; const char *wild; bool dir; - List<char> *files; + List<LEX_STRING> *files; }; static my_bool find_files_handlerton(THD *thd, plugin_ref plugin, @@ -2965,7 +2946,7 @@ static my_bool find_files_handlerton(THD *thd, plugin_ref plugin, int ha_find_files(THD *thd,const char *db,const char *path, - const char *wild, bool dir, List<char> *files) + const char *wild, bool dir, List<LEX_STRING> *files) { int error= 0; DBUG_ENTER("ha_find_files"); @@ -3297,7 +3278,8 @@ int handler::read_multi_range_next(KEY_MULTI_RANGE **found_range_p) read_range_first() start_key Start key. Is 0 if no min range end_key End key. Is 0 if no max range - eq_range_arg Set to 1 if start_key == end_key + eq_range_arg Set to 1 if start_key == end_key and the range endpoints + will not change during query execution. sorted Set to 1 if result should be sorted per key NOTES @@ -3329,10 +3311,10 @@ int handler::read_range_first(const key_range *start_key, if (!start_key) // Read first record result= index_first(table->record[0]); else - result= index_read(table->record[0], - start_key->key, - start_key->keypart_map, - start_key->flag); + result= index_read_map(table->record[0], + start_key->key, + start_key->keypart_map, + start_key->flag); if (result) DBUG_RETURN((result == HA_ERR_KEY_NOT_FOUND) ? HA_ERR_END_OF_FILE @@ -3404,15 +3386,15 @@ int handler::compare_key(key_range *range) } -int handler::index_read_idx(uchar * buf, uint index, const uchar * key, - key_part_map keypart_map, - enum ha_rkey_function find_flag) +int handler::index_read_idx_map(uchar * buf, uint index, const uchar * key, + key_part_map keypart_map, + enum ha_rkey_function find_flag) { int error, error1; error= index_init(index, 0); if (!error) { - error= index_read(buf, key, keypart_map, find_flag); + error= index_read_map(buf, key, keypart_map, find_flag); error1= index_end(); } return error ? error : error1; @@ -3578,8 +3560,7 @@ namespace { if (table->s->cached_row_logging_check == -1) { int const check(table->s->tmp_table == NO_TMP_TABLE && - binlog_filter->db_ok(table->s->db.str) && - !table->no_replicate); + binlog_filter->db_ok(table->s->db.str)); table->s->cached_row_logging_check= check; } @@ -3587,9 +3568,9 @@ namespace { table->s->cached_row_logging_check == 1); return (thd->current_stmt_binlog_row_based && + table->s->cached_row_logging_check && (thd->options & OPTION_BIN_LOG) && - mysql_bin_log.is_open() && - table->s->cached_row_logging_check); + mysql_bin_log.is_open()); } } @@ -3667,7 +3648,7 @@ namespace const uchar *before_record, const uchar *after_record) { - if (table->file->ha_table_flags() & HA_HAS_OWN_BINLOGGING) + if (table->no_replicate) return 0; bool error= 0; THD *const thd= table->in_use; @@ -3775,6 +3756,7 @@ int handler::ha_write_row(uchar *buf) return 0; } + int handler::ha_update_row(const uchar *old_data, uchar *new_data) { int error; diff --git a/sql/handler.h b/sql/handler.h index c498dd6e7af..55062b54532 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -335,13 +335,21 @@ typedef ulonglong my_xid; // this line is the same as in log_event.h #define MYSQL_XID_OFFSET (MYSQL_XID_PREFIX_LEN+sizeof(server_id)) #define MYSQL_XID_GTRID_LEN (MYSQL_XID_OFFSET+sizeof(my_xid)) -#define XIDDATASIZE 128 +#define XIDDATASIZE MYSQL_XIDDATASIZE #define MAXGTRIDSIZE 64 #define MAXBQUALSIZE 64 #define COMPATIBLE_DATA_YES 0 #define COMPATIBLE_DATA_NO 1 +/** + struct xid_t is binary compatible with the XID structure as + in the X/Open CAE Specification, Distributed Transaction Processing: + The XA Specification, X/Open Company Ltd., 1991. + http://www.opengroup.org/bookstore/catalog/c193.htm + + @see MYSQL_XID in mysql/plugin.h +*/ struct xid_t { long formatID; long gtrid_length; @@ -662,7 +670,7 @@ struct handlerton uint (*alter_table_flags)(uint flags); int (*alter_tablespace)(handlerton *hton, THD *thd, st_alter_tablespace *ts_info); int (*fill_files_table)(handlerton *hton, THD *thd, - struct st_table_list *tables, + TABLE_LIST *tables, class Item *cond); uint32 flags; /* global handler flags */ /* @@ -699,7 +707,7 @@ struct handlerton int (*find_files)(handlerton *hton, THD *thd, const char *db, const char *path, - const char *wild, bool dir, List<char> *files); + const char *wild, bool dir, List<LEX_STRING> *files); int (*table_exists_in_engine)(handlerton *hton, THD* thd, const char *db, const char *name); uint32 license; /* Flag for Engine License */ @@ -727,6 +735,35 @@ typedef struct st_thd_trans bool no_2pc; /* storage engines that registered themselves for this transaction */ handlerton *ht[MAX_HA]; + /* + The purpose of this flag is to keep track of non-transactional + tables that were modified in scope of: + - transaction, when the variable is a member of + THD::transaction.all + - top-level statement or sub-statement, when the variable is a + member of THD::transaction.stmt + This member has the following life cycle: + * stmt.modified_non_trans_table is used to keep track of + modified non-transactional tables of top-level statements. At + the end of the previous statement and at the beginning of the session, + it is reset to FALSE. If such functions + as mysql_insert, mysql_update, mysql_delete etc modify a + non-transactional table, they set this flag to TRUE. At the + end of the statement, the value of stmt.modified_non_trans_table + is merged with all.modified_non_trans_table and gets reset. + * all.modified_non_trans_table is reset at the end of transaction + + * Since we do not have a dedicated context for execution of a + sub-statement, to keep track of non-transactional changes in a + sub-statement, we re-use stmt.modified_non_trans_table. + At entrance into a sub-statement, a copy of the value of + stmt.modified_non_trans_table (containing the changes of the + outer statement) is saved on stack. Then + stmt.modified_non_trans_table is reset to FALSE and the + substatement is executed. Then the new value is merged with the + saved value. + */ + bool modified_non_trans_table; } THD_TRANS; enum enum_tx_isolation { ISO_READ_UNCOMMITTED, ISO_READ_COMMITTED, @@ -998,6 +1035,7 @@ public: uint ref_length; FT_INFO *ft_handler; enum {NONE=0, INDEX, RND} inited; + bool locked; bool implicit_emptied; /* Can be !=0 only if HEAP */ const COND *pushed_cond; /* @@ -1028,11 +1066,13 @@ public: estimation_rows_to_insert(0), ht(ht_arg), ref(0), key_used_on_scan(MAX_KEY), active_index(MAX_KEY), ref_length(sizeof(my_off_t)), - ft_handler(0), inited(NONE), implicit_emptied(0), + ft_handler(0), inited(NONE), + locked(FALSE), implicit_emptied(0), pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0) {} virtual ~handler(void) { + DBUG_ASSERT(locked == FALSE); /* TODO: DBUG_ASSERT(inited == NONE); */ } virtual handler *clone(MEM_ROOT *mem_root); @@ -1041,44 +1081,6 @@ public: { cached_table_flags= table_flags(); } - /* - Check whether a handler allows to lock the table. - - SYNOPSIS - check_if_locking_is_allowed() - thd Handler of the thread, trying to lock the table - table Table handler to check - count Total number of tables to be locked - current Index of the current table in the list of the tables - to be locked. - system_count Pointer to the counter of system tables seen thus - far. - 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, - MyISAM does not allow to lock mysql.proc along with other tables. - This limitation stems from the fact that MyISAM does not support - row-level locking and we have to add this limitation to avoid - deadlocks. - - RETURN - TRUE Locking is allowed - FALSE Locking is not allowed. The error was thrown. - */ - virtual bool check_if_locking_is_allowed(uint sql_command, - ulong type, TABLE *table, - uint count, uint current, - uint *system_count, - bool called_by_privileged_thread) - { - return TRUE; - } - bool check_if_log_table_locking_is_allowed(uint sql_command, - ulong type, TABLE *table); int ha_open(TABLE *table, const char *name, int mode, int test_if_locked); void adjust_next_insert_id_after_explicit_value(ulonglong nr); int update_auto_increment(); @@ -1289,27 +1291,28 @@ public: enum ha_rkey_function find_flag) { return HA_ERR_WRONG_COMMAND; } public: -/** - @brief - Positions an index cursor to the index specified in the handle. Fetches the - row if available. If the key value is null, begin at the first key of the - index. -*/ - virtual int index_read(uchar * buf, const uchar * key, key_part_map keypart_map, - enum ha_rkey_function find_flag) - { - uint key_len= calculate_key_len(table, active_index, key, keypart_map); - return index_read(buf, key, key_len, find_flag); - } -/** - @brief - Positions an index cursor to the index specified in the handle. Fetches the - row if available. If the key value is null, begin at the first key of the - index. -*/ - virtual int index_read_idx(uchar * buf, uint index, const uchar * key, + /** + @brief + Positions an index cursor to the index specified in the handle. Fetches the + row if available. If the key value is null, begin at the first key of the + index. + */ + virtual int index_read_map(uchar * buf, const uchar * key, key_part_map keypart_map, - enum ha_rkey_function find_flag); + enum ha_rkey_function find_flag) + { + uint key_len= calculate_key_len(table, active_index, key, keypart_map); + return index_read(buf, key, key_len, find_flag); + } + /** + @brief + Positions an index cursor to the index specified in the handle. Fetches the + row if available. If the key value is null, begin at the first key of the + index. + */ + virtual int index_read_idx_map(uchar * buf, uint index, const uchar * key, + key_part_map keypart_map, + enum ha_rkey_function find_flag); virtual int index_next(uchar * buf) { return HA_ERR_WRONG_COMMAND; } virtual int index_prev(uchar * buf) @@ -1323,17 +1326,17 @@ public: virtual int index_read_last(uchar * buf, const uchar * key, uint key_len) { return (my_errno=HA_ERR_WRONG_COMMAND); } public: -/** - @brief - The following functions works like index_read, but it find the last - row with the current key value or prefix. -*/ - virtual int index_read_last(uchar * buf, const uchar * key, - key_part_map keypart_map) - { - uint key_len= calculate_key_len(table, active_index, key, keypart_map); - return index_read_last(buf, key, key_len); - } + /** + @brief + The following functions works like index_read, but it find the last + row with the current key value or prefix. + */ + virtual int index_read_last_map(uchar * buf, const uchar * key, + key_part_map keypart_map) + { + uint key_len= calculate_key_len(table, active_index, key, keypart_map); + return index_read_last(buf, key, key_len); + } virtual int read_multi_range_first(KEY_MULTI_RANGE **found_range_p, KEY_MULTI_RANGE *ranges, uint range_count, bool sorted, HANDLER_BUFFER *buffer); @@ -1350,6 +1353,17 @@ public: virtual int ft_read(uchar *buf) { return HA_ERR_WRONG_COMMAND; } virtual int rnd_next(uchar *buf)=0; virtual int rnd_pos(uchar * buf, uchar *pos)=0; + /* + one has to use this method when to find + random position by record as the plain + position() call doesn't work for some + handlers for random position + */ + virtual int rnd_pos_by_record(uchar *record) + { + position(record); + return rnd_pos(record, ref); + } virtual int read_first_row(uchar *buf, uint primary_key); /* The following function is only needed for tables that may be temporary @@ -1635,8 +1649,10 @@ public: /* lock_count() can be more than one if the table is a MERGE */ virtual uint lock_count(void) const { return 1; } - /* - NOTE that one can NOT rely on table->in_use in store_lock(). It may + /** + Is not invoked for non-transactional temporary tables. + + @note that one can NOT rely on table->in_use in store_lock(). It may refer to a different thread if called from mysql_lock_abort_for_thread(). */ virtual THR_LOCK_DATA **store_lock(THD *thd, @@ -1645,16 +1661,49 @@ public: /* Type of table for caching query */ virtual uint8 table_cache_type() { return HA_CACHE_TBL_NONTRANSACT; } - /* ask handler about permission to cache table when query is to be cached */ + + + /** + @brief Register a named table with a call back function to the query cache. + + @param thd The thread handle + @param table_key A pointer to the table name in the table cache + @param key_length The length of the table name + @param[out] engine_callback The pointer to the storage engine call back + function + @param[out] engine_data Storage engine specific data which could be + anything + + This method offers the storage engine, the possibility to store a reference + to a table name which is going to be used with query cache. + The method is called each time a statement is written to the cache and can + be used to verify if a specific statement is cachable. It also offers + the possibility to register a generic (but static) call back function which + is called each time a statement is matched against the query cache. + + @note If engine_data supplied with this function is different from + engine_data supplied with the callback function, and the callback returns + FALSE, a table invalidation on the current table will occur. + + @return Upon success the engine_callback will point to the storage engine + call back function, if any, and engine_data will point to any storage + engine data used in the specific implementation. + @retval TRUE Success + @retval FALSE The specified table or current statement should not be + cached + */ + virtual my_bool register_query_cache_table(THD *thd, char *table_key, - uint key_length, - qc_engine_callback - *engine_callback, - ulonglong *engine_data) + uint key_length, + qc_engine_callback + *engine_callback, + ulonglong *engine_data) { *engine_callback= 0; - return 1; + return TRUE; } + + /* RETURN true Primary key (if there is one) is clustered key covering all fields @@ -1734,6 +1783,29 @@ private: overridden by the storage engine class. To call these methods, use the corresponding 'ha_*' method above. */ + + /** + Is not invoked for non-transactional temporary tables. + + Tells the storage engine that we intend to read or write data + from the table. This call is prefixed with a call to handler::store_lock() + and is invoked only for those handler instances that stored the lock. + + Calls to rnd_init/index_init are prefixed with this call. When table + IO is complete, we call external_lock(F_UNLCK). + A storage engine writer should expect that each call to + ::external_lock(F_[RD|WR]LOCK is followed by a call to + ::external_lock(F_UNLCK). If it is not, it is a bug in MySQL. + + The name and signature originate from the first implementation + in MyISAM, which would call fcntl to set/clear an advisory + lock on the data file in this method. + + @param lock_type F_RDLCK, F_WRLCK, F_UNLCK + + @return non-0 in case of failure, 0 in case of success. + When lock_type is F_UNLCK, the return value is ignored. + */ virtual int external_lock(THD *thd __attribute__((unused)), int lock_type __attribute__((unused))) { @@ -1815,11 +1887,11 @@ int ha_create_table_from_engine(THD* thd, const char *db, const char *name); int ha_discover(THD* thd, const char* dbname, const char* name, uchar** frmblob, size_t* frmlen); int ha_find_files(THD *thd,const char *db,const char *path, - const char *wild, bool dir,List<char>* files); + const char *wild, bool dir, List<LEX_STRING>* files); int ha_table_exists_in_engine(THD* thd, const char* db, const char* name); /* key cache */ -int ha_init_key_cache(const char *name, KEY_CACHE *key_cache); +extern "C" int ha_init_key_cache(const char *name, KEY_CACHE *key_cache); int ha_resize_key_cache(KEY_CACHE *key_cache); int ha_change_key_cache_param(KEY_CACHE *key_cache); int ha_change_key_cache(KEY_CACHE *old_key_cache, KEY_CACHE *new_key_cache); diff --git a/sql/item.cc b/sql/item.cc index d7743c491eb..d8080d62c39 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -334,6 +334,37 @@ int Item::save_date_in_field(Field *field) } +/* + Store the string value in field directly + + SYNOPSIS + Item::save_str_value_in_field() + field a pointer to field where to store + result the pointer to the string value to be stored + + DESCRIPTION + The method is used by Item_*::save_in_field implementations + when we don't need to calculate the value to store + See Item_string::save_in_field() implementation for example + + IMPLEMENTATION + Check if the Item is null and stores the NULL or the + result value in the field accordingly. + + RETURN + Nonzero value if error +*/ + +int Item::save_str_value_in_field(Field *field, String *result) +{ + if (null_value) + return set_field_to_null(field); + field->set_notnull(); + return field->store(result->ptr(), result->length(), + collation.collation); +} + + Item::Item(): rsize(0), name(0), orig_name(0), name_length(0), fixed(0), is_autogenerated_name(TRUE), @@ -684,10 +715,16 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) str++; } if (orig_len != length && !is_autogenerated_name) - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_REMOVED_SPACES, ER(ER_REMOVED_SPACES), - str + length - orig_len); - + { + if (length == 0) + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_NAME_BECOMES_EMPTY, ER(ER_NAME_BECOMES_EMPTY), + str + length - orig_len); + else + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_REMOVED_SPACES, ER(ER_REMOVED_SPACES), + str + length - orig_len); + } } if (!my_charset_same(cs, system_charset_info)) { @@ -1051,9 +1088,9 @@ bool Item_sp_variable::is_null() Item_splocal::Item_splocal(const LEX_STRING &sp_var_name, uint sp_var_idx, enum_field_types sp_var_type, - uint pos_in_q) + uint pos_in_q, uint len_in_q) :Item_sp_variable(sp_var_name.str, sp_var_name.length), - m_var_idx(sp_var_idx), pos_in_query(pos_in_q) + m_var_idx(sp_var_idx), pos_in_query(pos_in_q), len_in_query(len_in_q) { maybe_null= TRUE; @@ -1327,6 +1364,25 @@ void Item::split_sum_func2(THD *thd, Item **ref_pointer_array, } +static bool +left_is_superset(DTCollation *left, DTCollation *right) +{ + /* Allow convert to Unicode */ + if (left->collation->state & MY_CS_UNICODE && + (left->derivation < right->derivation || + (left->derivation == right->derivation && + !(right->collation->state & MY_CS_UNICODE)))) + return TRUE; + /* Allow convert from ASCII */ + if (right->repertoire == MY_REPERTOIRE_ASCII && + (left->derivation < right->derivation || + (left->derivation == right->derivation && + !(left->repertoire == MY_REPERTOIRE_ASCII)))) + return TRUE; + /* Disallow conversion otherwise */ + return FALSE; +} + /* Aggregate two collations together taking into account their coercibility (aka derivation): @@ -1391,18 +1447,12 @@ bool DTCollation::aggregate(DTCollation &dt, uint flags) ; // Do nothing } else if ((flags & MY_COLL_ALLOW_SUPERSET_CONV) && - collation->state & MY_CS_UNICODE && - (derivation < dt.derivation || - (derivation == dt.derivation && - !(dt.collation->state & MY_CS_UNICODE)))) + left_is_superset(this, &dt)) { // Do nothing } else if ((flags & MY_COLL_ALLOW_SUPERSET_CONV) && - dt.collation->state & MY_CS_UNICODE && - (dt.derivation < derivation || - (dt.derivation == derivation && - !(collation->state & MY_CS_UNICODE)))) + left_is_superset(&dt, this)) { set(dt); } @@ -1421,7 +1471,7 @@ bool DTCollation::aggregate(DTCollation &dt, uint flags) else { // Cannot apply conversion - set(0, DERIVATION_NONE); + set(0, DERIVATION_NONE, 0); return 1; } } @@ -1443,8 +1493,8 @@ bool DTCollation::aggregate(DTCollation &dt, uint flags) { if (derivation == DERIVATION_EXPLICIT) { - set(0, DERIVATION_NONE); - return 1; + set(0, DERIVATION_NONE, 0); + return 1; } if (collation->state & MY_CS_BINSORT) return 0; @@ -1458,6 +1508,7 @@ bool DTCollation::aggregate(DTCollation &dt, uint flags) set(bin, DERIVATION_NONE); } } + repertoire|= dt.repertoire; return 0; } @@ -1601,12 +1652,16 @@ bool agg_item_charsets(DTCollation &coll, const char *fname, { Item* conv; uint32 dummy_offset; - if (!String::needs_conversion(0, coll.collation, - (*arg)->collation.collation, + if (!String::needs_conversion(0, (*arg)->collation.collation, + coll.collation, &dummy_offset)) continue; - if (!(conv= (*arg)->safe_charset_converter(coll.collation))) + if (!(conv= (*arg)->safe_charset_converter(coll.collation)) && + ((*arg)->collation.repertoire == MY_REPERTOIRE_ASCII)) + conv= new Item_func_conv_charset(*arg, coll.collation, 1); + + if (!conv) { if (nargs >=2 && nargs <= 3) { @@ -1656,7 +1711,7 @@ void Item_ident_for_show::make_field(Send_field *tmp_field) tmp_field->type=field->type(); tmp_field->flags= field->table->maybe_null ? (field->flags & ~NOT_NULL_FLAG) : field->flags; - tmp_field->decimals= 0; + tmp_field->decimals= field->decimals(); } /**********************************************/ @@ -1752,6 +1807,8 @@ void Item_field::set_field(Field *field_par) unsigned_flag=test(field_par->flags & UNSIGNED_FLAG); collation.set(field_par->charset(), field_par->derivation()); fixed= 1; + if (field->table->s->tmp_table == SYSTEM_TMP_TABLE) + any_privileges= 0; } @@ -2018,6 +2075,11 @@ Item *Item_field::get_tmp_table_item(THD *thd) return new_item; } +longlong Item_field::val_int_endpoint(bool left_endp, bool *incl_endp) +{ + longlong res= val_int(); + return null_value? LONGLONG_MIN : res; +} /* Create an item from a string we KNOW points to a valid longlong @@ -2349,7 +2411,7 @@ default_set_param_func(Item_param *param, } -Item_param::Item_param(unsigned pos_in_query_arg) : +Item_param::Item_param(uint pos_in_query_arg) : strict_type(FALSE), state(NO_VALUE), item_result_type(STRING_RESULT), @@ -3046,16 +3108,6 @@ my_decimal *Item_copy_string::val_decimal(my_decimal *decimal_value) } - -int Item_copy_string::save_in_field(Field *field, bool no_conversions) -{ - if (null_value) - return set_field_to_null(field); - field->set_notnull(); - return field->store(str_value.ptr(),str_value.length(), - collation.collation); -} - /* Functions to convert item to field (for send_fields) */ @@ -3830,15 +3882,15 @@ bool Item_field::fix_fields(THD *thd, Item **reference) 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. + 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; + Field *new_field= (*((Item_field**)res))->field; - if (field == NULL) + if (new_field == NULL) { /* The column to which we link isn't valid. */ my_error(ER_BAD_FIELD_ERROR, MYF(0), (*res)->name, @@ -3846,7 +3898,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) return(1); } - set_field(field); + set_field(new_field); return 0; } else @@ -4508,6 +4560,12 @@ int Item_null::save_safe_in_field(Field *field) } +/* + This implementation can lose str_value content, so if the + Item uses str_value to store something, it should + reimplement it's ::save_in_field() as Item_string, for example, does +*/ + int Item::save_in_field(Field *field, bool no_conversions) { int error; @@ -4565,10 +4623,7 @@ int Item_string::save_in_field(Field *field, bool no_conversions) { String *result; result=val_str(&str_value); - if (null_value) - return set_field_to_null(field); - field->set_notnull(); - return field->store(result->ptr(),result->length(),collation.collation); + return save_str_value_in_field(field, result); } @@ -5899,7 +5954,7 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items) if (!arg->fixed) { bool res; - st_table_list *orig_next_table= context->last_name_resolution_table; + TABLE_LIST *orig_next_table= context->last_name_resolution_table; context->last_name_resolution_table= context->first_name_resolution_table; res= arg->fix_fields(thd, &arg); context->last_name_resolution_table= orig_next_table; diff --git a/sql/item.h b/sql/item.h index 6df85476f03..655159ab752 100644 --- a/sql/item.h +++ b/sql/item.h @@ -19,7 +19,7 @@ #endif class Protocol; -struct st_table_list; +struct TABLE_LIST; void item_init(void); /* Init item functions */ class Item_field; @@ -49,29 +49,50 @@ class DTCollation { public: CHARSET_INFO *collation; enum Derivation derivation; + uint repertoire; + void set_repertoire_from_charset(CHARSET_INFO *cs) + { + repertoire= cs->state & MY_CS_PUREASCII ? + MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30; + } DTCollation() { collation= &my_charset_bin; derivation= DERIVATION_NONE; + repertoire= MY_REPERTOIRE_UNICODE30; } DTCollation(CHARSET_INFO *collation_arg, Derivation derivation_arg) { collation= collation_arg; derivation= derivation_arg; + set_repertoire_from_charset(collation_arg); } void set(DTCollation &dt) { collation= dt.collation; derivation= dt.derivation; + repertoire= dt.repertoire; } void set(CHARSET_INFO *collation_arg, Derivation derivation_arg) { collation= collation_arg; derivation= derivation_arg; + set_repertoire_from_charset(collation_arg); + } + void set(CHARSET_INFO *collation_arg, + Derivation derivation_arg, + uint repertoire_arg) + { + collation= collation_arg; + derivation= derivation_arg; + repertoire= repertoire_arg; } void set(CHARSET_INFO *collation_arg) - { collation= collation_arg; } + { + collation= collation_arg; + set_repertoire_from_charset(collation_arg); + } void set(Derivation derivation_arg) { derivation= derivation_arg; } bool aggregate(DTCollation &dt, uint flags= 0); @@ -548,6 +569,43 @@ public: virtual enum_monotonicity_info get_monotonicity_info() const { return NON_MONOTONIC; } + /* + Convert "func_arg $CMP$ const" half-interval into "FUNC(func_arg) $CMP2$ const2" + + SYNOPSIS + val_int_endpoint() + left_endp FALSE <=> The interval is "x < const" or "x <= const" + TRUE <=> The interval is "x > const" or "x >= const" + + incl_endp IN TRUE <=> the comparison is '<' or '>' + FALSE <=> the comparison is '<=' or '>=' + OUT The same but for the "F(x) $CMP$ F(const)" comparison + + DESCRIPTION + This function is defined only for unary monotonic functions. The caller + supplies the source half-interval + + x $CMP$ const + + The value of const is supplied implicitly as the value this item's + argument, the form of $CMP$ comparison is specified through the + function's arguments. The calle returns the result interval + + F(x) $CMP2$ F(const) + + passing back F(const) as the return value, and the form of $CMP2$ + through the out parameter. NULL values are assumed to be comparable and + be less than any non-NULL values. + + RETURN + The output range bound, which equal to the value of val_int() + - If the value of the function is NULL then the bound is the + smallest possible value of LONGLONG_MIN + */ + virtual longlong val_int_endpoint(bool left_endp, bool *incl_endp) + { DBUG_ASSERT(0); return 0; } + + /* valXXX methods must return NULL or 0 or 0.0 if null_value is set. */ /* Return double precision floating point representation of item. @@ -653,6 +711,7 @@ public: int save_time_in_field(Field *field); int save_date_in_field(Field *field); + int save_str_value_in_field(Field *field, String *result); virtual Field *get_tmp_table_field() { return 0; } /* This is also used to create fields in CREATE ... SELECT: */ @@ -843,8 +902,7 @@ public: german character for double s is equal to 2 s. The default is that an item is not allowed - in a partition function. However all mathematical functions, string - manipulation functions, date functions are allowed. Allowed functions + in a partition function. Allowed functions can never depend on server version, they cannot depend on anything related to the environment. They can also only depend on a set of fields in the table itself. They cannot depend on other tables and @@ -1054,9 +1112,18 @@ public: SP variable in query text. */ uint pos_in_query; + /* + Byte length of SP variable name in the statement (see pos_in_query). + The value of this field may differ from the name_length value because + name_length contains byte length of UTF8-encoded item name, but + the query string (see sp_instr_stmt::m_query) is currently stored with + a charset from the SET NAMES statement. + */ + uint len_in_query; Item_splocal(const LEX_STRING &sp_var_name, uint sp_var_idx, - enum_field_types sp_var_type, uint pos_in_q= 0); + enum_field_types sp_var_type, + uint pos_in_q= 0, uint len_in_q= 0); bool is_splocal() { return 1; } /* Needed for error checking */ @@ -1371,6 +1438,7 @@ public: { return MONOTONIC_STRICT_INCREASING; } + longlong val_int_endpoint(bool left_endp, bool *incl_endp); Field *get_tmp_table_field() { return result_field; } Field *tmp_table_field(TABLE *t_arg) { return result_field; } bool get_date(MYSQL_TIME *ltime,uint fuzzydate); @@ -1633,6 +1701,7 @@ public: uint decimal_precision() const { return (uint)(max_length - test(value < 0)); } bool eq(const Item *, bool binary_cmp) const; + bool check_partition_func_processor(uchar *bool_arg) { return FALSE;} }; @@ -1650,6 +1719,7 @@ public: void print(String *str); Item_num *neg (); uint decimal_precision() const { return max_length; } + bool check_partition_func_processor(uchar *bool_arg) { return FALSE;} }; @@ -1692,6 +1762,7 @@ public: uint decimal_precision() const { return decimal_value.precision(); } bool eq(const Item *, bool binary_cmp) const; void set_decimal_value(my_decimal *value_par); + bool check_partition_func_processor(uchar *bool_arg) { return FALSE;} }; @@ -1710,8 +1781,11 @@ public: max_length=length; fixed= 1; } - Item_float(double value_par) :presentation(0), value(value_par) { fixed= 1; } - + Item_float(double value_par, uint decimal_par) :presentation(0), value(value_par) + { + decimals= (uint8) decimal_par; + fixed= 1; + } int save_in_field(Field *field, bool no_conversions); enum Type type() const { return REAL_ITEM; } enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } @@ -1752,7 +1826,6 @@ public: {} void print(String *str) { str->append(func_name); } Item *safe_charset_converter(CHARSET_INFO *tocs); - bool check_partition_func_processor(uchar *int_arg) {return TRUE;} }; @@ -1760,10 +1833,11 @@ class Item_string :public Item { public: Item_string(const char *str,uint length, - CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE) + CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE, + uint repertoire= MY_REPERTOIRE_UNICODE30) { - collation.set(cs, dv); - str_value.set_or_copy_aligned(str,length,cs); + str_value.set_or_copy_aligned(str, length, cs); + collation.set(cs, dv, repertoire); /* We have to have a different max_length than 'length' here to ensure that we get the right length if we do use the item @@ -1787,10 +1861,11 @@ public: fixed= 1; } Item_string(const char *name_par, const char *str, uint length, - CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE) + CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE, + uint repertoire= MY_REPERTOIRE_UNICODE30) { - collation.set(cs, dv); - str_value.set_or_copy_aligned(str,length,cs); + str_value.set_or_copy_aligned(str, length, cs); + collation.set(cs, dv, repertoire); max_length= str_value.numchars()*cs->mbmaxlen; set_name(name_par, 0, cs); decimals=NOT_FIXED_DEC; @@ -1806,6 +1881,12 @@ public: str_value.copy(str_arg, length_arg, collation.collation); max_length= str_value.numchars() * collation.collation->mbmaxlen; } + void set_repertoire_from_value() + { + collation.repertoire= my_string_repertoire(str_value.charset(), + str_value.ptr(), + str_value.length()); + } enum Type type() const { return STRING_ITEM; } double val_real(); longlong val_int(); @@ -1861,7 +1942,6 @@ public: CHARSET_INFO *cs= NULL): Item_string(name, length, cs) {} - bool check_partition_func_processor(uchar *int_arg) {return TRUE;} }; @@ -1915,7 +1995,6 @@ public: unsigned_flag=1; } enum_field_types field_type() const { return int_field_type; } - bool check_partition_func_processor(uchar *int_arg) {return TRUE;} }; @@ -2116,6 +2195,12 @@ public: bool fix_fields(THD *, Item **); bool eq(const Item *item, bool binary_cmp) const; + Item *get_tmp_table_item(THD *thd) + { + Item *item= Item_ref::get_tmp_table_item(thd); + item->name= name; + return item; + } virtual Ref_Type ref_type() { return VIEW_REF; } }; @@ -2237,7 +2322,6 @@ public: } Item *clone_item(); virtual Item *real_item() { return ref; } - bool check_partition_func_processor(uchar *int_arg) {return TRUE;} }; #ifdef MYSQL_SERVER @@ -2289,7 +2373,10 @@ public: my_decimal *val_decimal(my_decimal *); void make_field(Send_field *field) { item->make_field(field); } void copy(); - int save_in_field(Field *field, bool no_conversions); + int save_in_field(Field *field, bool no_conversions) + { + return save_str_value_in_field(field, &str_value); + } table_map used_tables() const { return (table_map) 1L; } bool const_item() const { return 0; } bool is_null() { return null_value; } @@ -2438,14 +2525,6 @@ enum trg_action_time_type TRG_ACTION_BEFORE= 0, TRG_ACTION_AFTER= 1, TRG_ACTION_MAX }; -/* - Event on which trigger is invoked. -*/ -enum trg_event_type -{ - TRG_EVENT_INSERT= 0 , TRG_EVENT_UPDATE= 1, TRG_EVENT_DELETE= 2, TRG_EVENT_MAX -}; - class Table_triggers_list; /* diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 3477ce84b21..8f5ff050dd6 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -719,6 +719,67 @@ Arg_comparator::can_compare_as_dates(Item *a, Item *b, ulonglong *const_value) } +/* + Retrieves correct TIME value from the given item. + + SYNOPSIS + get_time_value() + thd thread handle + item_arg [in/out] item to retrieve TIME value from + cache_arg [in/out] pointer to place to store the cache item to + warn_item [in] unused + is_null [out] TRUE <=> the item_arg is null + + DESCRIPTION + Retrieves the correct TIME value from given item for comparison by the + compare_datetime() function. + If item's result can be compared as longlong then its int value is used + and a value returned by get_time function is used otherwise. + If an item is a constant one then its value is cached and it isn't + get parsed again. An Item_cache_int object is used for for cached values. + It seamlessly substitutes the original item. The cache item is marked as + non-constant to prevent re-caching it again. + + RETURN + obtained value +*/ + +ulonglong +get_time_value(THD *thd, Item ***item_arg, Item **cache_arg, + Item *warn_item, bool *is_null) +{ + ulonglong value; + Item *item= **item_arg; + MYSQL_TIME ltime; + + if (item->result_as_longlong()) + { + value= item->val_int(); + *is_null= item->null_value; + } + else + { + *is_null= item->get_time(<ime); + value= !*is_null ? TIME_to_ulonglong_datetime(<ime) : 0; + } + /* + Do not cache GET_USER_VAR() function as its const_item() may return TRUE + for the current thread but it still may change during the execution. + */ + if (item->const_item() && cache_arg && (item->type() != Item::FUNC_ITEM || + ((Item_func*)item)->functype() != Item_func::GUSERVAR_FUNC)) + { + Item_cache_int *cache= new Item_cache_int(); + /* Mark the cache as non-const to prevent re-caching. */ + cache->set_used_tables(1); + cache->store(item, value); + *cache_arg= cache; + *item_arg= cache_arg; + } + return value; +} + + int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg, Item **a1, Item **a2, Item_result type) @@ -757,8 +818,23 @@ int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg, } is_nulls_eq= test(owner && owner->functype() == Item_func::EQUAL_FUNC); func= &Arg_comparator::compare_datetime; + get_value_func= &get_datetime_value; return 0; } + else if (type == STRING_RESULT && (*a)->field_type() == MYSQL_TYPE_TIME && + (*b)->field_type() == MYSQL_TYPE_TIME) + { + /* Compare TIME values as integers. */ + thd= current_thd; + owner= owner_arg; + a_cache= 0; + b_cache= 0; + is_nulls_eq= test(owner && owner->functype() == Item_func::EQUAL_FUNC); + func= &Arg_comparator::compare_datetime; + get_value_func= &get_time_value; + return 0; + } + return set_compare_func(owner_arg, type); } @@ -776,8 +852,10 @@ void Arg_comparator::set_datetime_cmp_func(Item **a1, Item **b1) b_cache= 0; is_nulls_eq= FALSE; func= &Arg_comparator::compare_datetime; + get_value_func= &get_datetime_value; } + /* Retrieves correct DATETIME value from given item. @@ -891,8 +969,8 @@ int Arg_comparator::compare_datetime() bool is_null= FALSE; ulonglong a_value, b_value; - /* Get DATE/DATETIME value of the 'a' item. */ - a_value= get_datetime_value(thd, &a, &a_cache, *b, &is_null); + /* Get DATE/DATETIME/TIME value of the 'a' item. */ + a_value= (*get_value_func)(thd, &a, &a_cache, *b, &is_null); if (!is_nulls_eq && is_null) { if (owner) @@ -900,8 +978,8 @@ int Arg_comparator::compare_datetime() return -1; } - /* Get DATE/DATETIME value of the 'b' item. */ - b_value= get_datetime_value(thd, &b, &b_cache, *a, &is_null); + /* Get DATE/DATETIME/TIME value of the 'b' item. */ + b_value= (*get_value_func)(thd, &b, &b_cache, *a, &is_null); if (is_null) { if (owner) @@ -1771,6 +1849,7 @@ void Item_func_between::fix_length_and_dec() max_length= 1; int i; bool datetime_found= FALSE; + int time_items_found= 0; compare_as_dates= TRUE; THD *thd= current_thd; @@ -1791,17 +1870,19 @@ void Item_func_between::fix_length_and_dec() At least one of items should be a DATE/DATETIME item and other items should return the STRING result. */ - for (i= 0; i < 3; i++) + if (cmp_type == STRING_RESULT) { - if (args[i]->is_datetime()) + for (i= 0; i < 3; i++) { - datetime_found= TRUE; - continue; + if (args[i]->is_datetime()) + { + datetime_found= TRUE; + continue; + } + if (args[i]->field_type() == MYSQL_TYPE_TIME && + args[i]->result_as_longlong()) + time_items_found++; } - if (args[i]->result_type() == STRING_RESULT) - continue; - compare_as_dates= FALSE; - break; } if (!datetime_found) compare_as_dates= FALSE; @@ -1811,6 +1892,11 @@ void Item_func_between::fix_length_and_dec() ge_cmp.set_datetime_cmp_func(args, args + 1); le_cmp.set_datetime_cmp_func(args, args + 2); } + else if (time_items_found == 3) + { + /* Compare TIME items as integers. */ + cmp_type= INT_RESULT; + } else if (args[0]->real_item()->type() == FIELD_ITEM && thd->lex->sql_command != SQLCOM_CREATE_VIEW && thd->lex->sql_command != SQLCOM_SHOW_CREATE) @@ -2931,11 +3017,11 @@ uchar *in_longlong::get_value(Item *item) void in_datetime::set(uint pos,Item *item) { - Item **tmp= &item; + Item **tmp_item= &item; bool is_null; struct packed_longlong *buff= &((packed_longlong*) base)[pos]; - buff->val= get_datetime_value(thd, &tmp, 0, warn_item, &is_null); + buff->val= get_datetime_value(thd, &tmp_item, 0, warn_item, &is_null); buff->unsigned_flag= 1L; } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 1bc52ea093c..40d0c2dfe91 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -42,6 +42,8 @@ class Arg_comparator: public Sql_alloc bool is_nulls_eq; // TRUE <=> compare for the EQUAL_FUNC enum enum_date_cmp_type { CMP_DATE_DFLT= 0, CMP_DATE_WITH_DATE, CMP_DATE_WITH_STR, CMP_STR_WITH_DATE }; + ulonglong (*get_value_func)(THD *thd, Item ***item_arg, Item **cache_arg, + Item *warn_item, bool *is_null); public: DTCollation cmp_collation; @@ -355,7 +357,6 @@ public: } Item *neg_transformer(THD *thd); virtual Item *negated_item(); - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool subst_argument_checker(uchar **arg) { return TRUE; } }; @@ -367,7 +368,6 @@ public: enum Functype functype() const { return NOT_FUNC; } const char *func_name() const { return "not"; } Item *neg_transformer(THD *thd); - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} void print(String *str); }; @@ -598,7 +598,6 @@ public: bool is_bool_func() { return 1; } CHARSET_INFO *compare_collation() { return cmp_collation.collation; } uint decimal_precision() const { return 1; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -610,7 +609,6 @@ public: optimize_type select_optimize() const { return OPTIMIZE_NONE; } const char *func_name() const { return "strcmp"; } void print(String *str) { Item_func::print(str); } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -673,7 +671,6 @@ public: const char *func_name() const { return "ifnull"; } Field *tmp_table_field(TABLE *table); uint decimal_precision() const; - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -714,7 +711,6 @@ public: void print(String *str) { Item_func::print(str); } table_map not_null_tables() const { return 0; } bool is_null(); - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; /* Functions to handle the optimized IN */ @@ -857,6 +853,7 @@ public: friend int cmp_longlong(void *cmp_arg, packed_longlong *a,packed_longlong *b); }; + class in_double :public in_vector { double tmp; @@ -866,16 +863,16 @@ public: uchar *get_value(Item *item); Item *create_item() { - return new Item_float(0.0); + return new Item_float(0.0, 0); } void value_to_item(uint pos, Item *item) { ((Item_float*)item)->value= ((double*) base)[pos]; } Item_result result_type() { return REAL_RESULT; } - }; + class in_decimal :public in_vector { my_decimal val; @@ -1141,7 +1138,6 @@ public: void print(String *str); Item *find_item(String *str); CHARSET_INFO *compare_collation() { return cmp_collation.collation; } - bool check_partition_func_processor(uchar *bool_arg) { return FALSE;} void cleanup(); }; @@ -1162,7 +1158,6 @@ 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 @@ -1211,7 +1206,6 @@ public: bool nulls_in_row(); bool is_bool_func() { return 1; } CHARSET_INFO *compare_collation() { return cmp_collation.collation; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class cmp_item_row :public cmp_item @@ -1283,7 +1277,6 @@ public: optimize_type select_optimize() const { return OPTIMIZE_NULL; } Item *neg_transformer(THD *thd); CHARSET_INFO *compare_collation() { return args[0]->collation.collation; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; /* Functions used by HAVING for rewriting IN subquery */ @@ -1310,7 +1303,6 @@ public: */ table_map used_tables() const { return used_tables_cache | RAND_TABLE_BIT; } - bool check_partition_func_processor(uchar *int_arg) {return TRUE;} }; @@ -1333,7 +1325,6 @@ public: void print(String *str); CHARSET_INFO *compare_collation() { return args[0]->collation.collation; } void top_level_item() { abort_on_null=1; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -1372,7 +1363,6 @@ public: const char *func_name() const { return "like"; } bool fix_fields(THD *thd, Item **ref); void cleanup(); - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; #ifdef USE_REGEX @@ -1395,7 +1385,6 @@ public: const char *func_name() const { return "regexp"; } void print(String *str) { print_op(str); } CHARSET_INFO *compare_collation() { return cmp_collation.collation; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; #else @@ -1435,6 +1424,7 @@ public: Item_cond(List<Item> &nlist) :Item_bool_func(), list(nlist), abort_on_null(0) {} bool add(Item *item) { return list.push_back(item); } + bool add_at_head(Item *item) { return list.push_front(item); } void add_at_head(List<Item> *nlist) { list.prepand(nlist); } bool fix_fields(THD *, Item **ref); @@ -1452,7 +1442,6 @@ public: Item *transform(Item_transformer transformer, uchar *arg); void traverse_cond(Cond_traverser, void *arg, traverse_order order); void neg_arguments(THD *thd); - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool subst_argument_checker(uchar **arg) { return TRUE; } Item *compile(Item_analyzer analyzer, uchar **arg_p, Item_transformer transformer, uchar *arg_t); @@ -1581,7 +1570,6 @@ public: the current and level */ COND_EQUAL() { - max_members= 0; upper_levels= 0; } }; @@ -1629,6 +1617,15 @@ public: Item *neg_transformer(THD *thd); }; +inline bool is_cond_and(Item *item) +{ + if (item->type() != Item::COND_ITEM) + return FALSE; + + Item_cond *cond_item= (Item_cond*) item; + return (cond_item->functype() == Item_func::COND_AND_FUNC); +} + class Item_cond_or :public Item_cond { public: @@ -1650,6 +1647,14 @@ public: Item *neg_transformer(THD *thd); }; +inline bool is_cond_or(Item *item) +{ + if (item->type() != Item::COND_ITEM) + return FALSE; + + Item_cond *cond_item= (Item_cond*) item; + return (cond_item->functype() == Item_func::COND_OR_FUNC); +} /* XOR is Item_cond, not an Item_int_func because we could like to diff --git a/sql/item_create.cc b/sql/item_create.cc index 20041b1176a..12b795e79a1 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -37,7 +37,7 @@ class Create_native_func : public Create_func { public: - virtual Item* create(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create(THD *thd, LEX_STRING name, List<Item> *item_list); /** Builder method, with no arguments. @@ -46,7 +46,7 @@ public: @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, + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list) = 0; protected: @@ -64,14 +64,14 @@ protected: class Create_func_arg0 : public Create_func { public: - virtual Item* create(THD *thd, LEX_STRING name, List<Item> *item_list); + 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; + virtual Item *create(THD *thd) = 0; protected: /** Constructor. */ @@ -88,7 +88,7 @@ protected: class Create_func_arg1 : public Create_func { public: - virtual Item* create(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create(THD *thd, LEX_STRING name, List<Item> *item_list); /** Builder method, with one argument. @@ -96,7 +96,7 @@ public: @param arg1 The first argument of the function @return An item representing the function call */ - virtual Item* create(THD *thd, Item *arg1) = 0; + virtual Item *create(THD *thd, Item *arg1) = 0; protected: /** Constructor. */ @@ -113,7 +113,7 @@ protected: class Create_func_arg2 : public Create_func { public: - virtual Item* create(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create(THD *thd, LEX_STRING name, List<Item> *item_list); /** Builder method, with two arguments. @@ -122,7 +122,7 @@ public: @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; + virtual Item *create(THD *thd, Item *arg1, Item *arg2) = 0; protected: /** Constructor. */ @@ -139,7 +139,7 @@ protected: class Create_func_arg3 : public Create_func { public: - virtual Item* create(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create(THD *thd, LEX_STRING name, List<Item> *item_list); /** Builder method, with three arguments. @@ -149,7 +149,7 @@ public: @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; + virtual Item *create(THD *thd, Item *arg1, Item *arg2, Item *arg3) = 0; protected: /** Constructor. */ @@ -166,7 +166,7 @@ protected: class Create_sp_func : public Create_qfunc { public: - virtual Item* create(THD *thd, LEX_STRING db, LEX_STRING name, + virtual Item *create(THD *thd, LEX_STRING db, LEX_STRING name, bool use_explicit_name, List<Item> *item_list); static Create_sp_func s_singleton; @@ -189,7 +189,7 @@ protected: class Create_func_no_geom : public Create_func { public: - virtual Item* create(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create(THD *thd, LEX_STRING name, List<Item> *item_list); /** Singleton. */ static Create_func_no_geom s_singleton; @@ -212,7 +212,7 @@ protected: class Create_func_abs : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_abs s_singleton; @@ -225,7 +225,7 @@ protected: class Create_func_acos : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_acos s_singleton; @@ -238,7 +238,7 @@ protected: class Create_func_addtime : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_addtime s_singleton; @@ -251,7 +251,7 @@ protected: class Create_func_aes_encrypt : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_aes_encrypt s_singleton; @@ -264,7 +264,7 @@ protected: class Create_func_aes_decrypt : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_aes_decrypt s_singleton; @@ -278,7 +278,7 @@ protected: class Create_func_area : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_area s_singleton; @@ -293,7 +293,7 @@ protected: class Create_func_as_wkb : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_as_wkb s_singleton; @@ -308,7 +308,7 @@ protected: class Create_func_as_wkt : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_as_wkt s_singleton; @@ -322,7 +322,7 @@ protected: class Create_func_asin : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_asin s_singleton; @@ -335,7 +335,7 @@ protected: class Create_func_atan : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_atan s_singleton; @@ -348,7 +348,7 @@ protected: class Create_func_benchmark : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_benchmark s_singleton; @@ -361,7 +361,7 @@ protected: class Create_func_bin : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_bin s_singleton; @@ -374,7 +374,7 @@ protected: class Create_func_bit_count : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_bit_count s_singleton; @@ -387,7 +387,7 @@ protected: class Create_func_bit_length : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_bit_length s_singleton; @@ -400,7 +400,7 @@ protected: class Create_func_ceiling : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg); + virtual Item *create(THD *thd, Item *arg1); static Create_func_ceiling s_singleton; @@ -414,7 +414,7 @@ protected: class Create_func_centroid : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_centroid s_singleton; @@ -428,7 +428,7 @@ protected: class Create_func_char_length : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_char_length s_singleton; @@ -441,7 +441,7 @@ protected: class Create_func_coercibility : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_coercibility s_singleton; @@ -454,7 +454,7 @@ protected: class Create_func_compress : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_compress s_singleton; @@ -467,7 +467,7 @@ protected: class Create_func_concat : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_concat s_singleton; @@ -480,7 +480,7 @@ protected: class Create_func_concat_ws : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_concat_ws s_singleton; @@ -493,7 +493,7 @@ protected: class Create_func_connection_id : public Create_func_arg0 { public: - virtual Item* create(THD *thd); + virtual Item *create(THD *thd); static Create_func_connection_id s_singleton; @@ -507,7 +507,7 @@ protected: class Create_func_contains : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_contains s_singleton; @@ -521,7 +521,7 @@ protected: class Create_func_conv : public Create_func_arg3 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2, Item *arg3); + virtual Item *create(THD *thd, Item *arg1, Item *arg2, Item *arg3); static Create_func_conv s_singleton; @@ -534,7 +534,7 @@ protected: class Create_func_convert_tz : public Create_func_arg3 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2, Item *arg3); + virtual Item *create(THD *thd, Item *arg1, Item *arg2, Item *arg3); static Create_func_convert_tz s_singleton; @@ -547,7 +547,7 @@ protected: class Create_func_cos : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_cos s_singleton; @@ -560,7 +560,7 @@ protected: class Create_func_cot : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_cot s_singleton; @@ -573,7 +573,7 @@ protected: class Create_func_crc32 : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_crc32 s_singleton; @@ -587,7 +587,7 @@ protected: class Create_func_crosses : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_crosses s_singleton; @@ -601,7 +601,7 @@ protected: class Create_func_date_format : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_date_format s_singleton; @@ -614,7 +614,7 @@ protected: class Create_func_datediff : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_datediff s_singleton; @@ -627,7 +627,7 @@ protected: class Create_func_dayname : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_dayname s_singleton; @@ -640,7 +640,7 @@ protected: class Create_func_dayofmonth : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_dayofmonth s_singleton; @@ -653,7 +653,7 @@ protected: class Create_func_dayofweek : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_dayofweek s_singleton; @@ -666,7 +666,7 @@ protected: class Create_func_dayofyear : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_dayofyear s_singleton; @@ -679,7 +679,7 @@ protected: class Create_func_decode : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_decode s_singleton; @@ -692,7 +692,7 @@ protected: class Create_func_degrees : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_degrees s_singleton; @@ -705,7 +705,7 @@ protected: class Create_func_des_decrypt : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_des_decrypt s_singleton; @@ -718,7 +718,7 @@ protected: class Create_func_des_encrypt : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_des_encrypt s_singleton; @@ -732,7 +732,7 @@ protected: class Create_func_dimension : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_dimension s_singleton; @@ -747,7 +747,7 @@ protected: class Create_func_disjoint : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_disjoint s_singleton; @@ -761,7 +761,7 @@ protected: class Create_func_elt : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_elt s_singleton; @@ -774,7 +774,7 @@ protected: class Create_func_encode : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_encode s_singleton; @@ -787,7 +787,7 @@ protected: class Create_func_encrypt : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_encrypt s_singleton; @@ -801,7 +801,7 @@ protected: class Create_func_endpoint : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_endpoint s_singleton; @@ -816,7 +816,7 @@ protected: class Create_func_envelope : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_envelope s_singleton; @@ -831,7 +831,7 @@ protected: class Create_func_equals : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_equals s_singleton; @@ -845,7 +845,7 @@ protected: class Create_func_exp : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_exp s_singleton; @@ -858,7 +858,7 @@ protected: class Create_func_export_set : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_export_set s_singleton; @@ -872,7 +872,7 @@ protected: class Create_func_exteriorring : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_exteriorring s_singleton; @@ -886,7 +886,7 @@ protected: class Create_func_field : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_field s_singleton; @@ -899,7 +899,7 @@ protected: class Create_func_find_in_set : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_find_in_set s_singleton; @@ -912,7 +912,7 @@ protected: class Create_func_floor : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_floor s_singleton; @@ -925,7 +925,7 @@ protected: class Create_func_format : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_format s_singleton; @@ -938,7 +938,7 @@ protected: class Create_func_found_rows : public Create_func_arg0 { public: - virtual Item* create(THD *thd); + virtual Item *create(THD *thd); static Create_func_found_rows s_singleton; @@ -951,7 +951,7 @@ protected: class Create_func_from_days : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_from_days s_singleton; @@ -964,7 +964,7 @@ protected: class Create_func_from_unixtime : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_from_unixtime s_singleton; @@ -978,7 +978,7 @@ protected: class Create_func_geometry_from_text : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_geometry_from_text s_singleton; @@ -993,7 +993,7 @@ protected: class Create_func_geometry_from_wkb : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_geometry_from_wkb s_singleton; @@ -1008,7 +1008,7 @@ protected: class Create_func_geometry_type : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_geometry_type s_singleton; @@ -1023,7 +1023,7 @@ protected: class Create_func_geometryn : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_geometryn s_singleton; @@ -1037,7 +1037,7 @@ protected: class Create_func_get_lock : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_get_lock s_singleton; @@ -1051,7 +1051,7 @@ protected: class Create_func_glength : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_glength s_singleton; @@ -1065,7 +1065,7 @@ protected: class Create_func_greatest : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_greatest s_singleton; @@ -1078,7 +1078,7 @@ protected: class Create_func_hex : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_hex s_singleton; @@ -1091,7 +1091,7 @@ protected: class Create_func_ifnull : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_ifnull s_singleton; @@ -1104,7 +1104,7 @@ protected: class Create_func_inet_ntoa : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_inet_ntoa s_singleton; @@ -1117,7 +1117,7 @@ protected: class Create_func_inet_aton : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_inet_aton s_singleton; @@ -1130,7 +1130,7 @@ protected: class Create_func_instr : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_instr s_singleton; @@ -1144,7 +1144,7 @@ protected: class Create_func_interiorringn : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_interiorringn s_singleton; @@ -1159,7 +1159,7 @@ protected: class Create_func_intersects : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_intersects s_singleton; @@ -1173,7 +1173,7 @@ protected: class Create_func_is_free_lock : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_is_free_lock s_singleton; @@ -1186,7 +1186,7 @@ protected: class Create_func_is_used_lock : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_is_used_lock s_singleton; @@ -1200,7 +1200,7 @@ protected: class Create_func_isclosed : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_isclosed s_singleton; @@ -1215,7 +1215,7 @@ protected: class Create_func_isempty : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_isempty s_singleton; @@ -1229,7 +1229,7 @@ protected: class Create_func_isnull : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_isnull s_singleton; @@ -1243,7 +1243,7 @@ protected: class Create_func_issimple : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_issimple s_singleton; @@ -1257,7 +1257,7 @@ protected: class Create_func_last_day : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_last_day s_singleton; @@ -1270,7 +1270,7 @@ protected: class Create_func_last_insert_id : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_last_insert_id s_singleton; @@ -1283,7 +1283,7 @@ protected: class Create_func_lcase : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_lcase s_singleton; @@ -1296,7 +1296,7 @@ protected: class Create_func_least : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_least s_singleton; @@ -1309,7 +1309,7 @@ protected: class Create_func_length : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_length s_singleton; @@ -1322,7 +1322,7 @@ protected: class Create_func_ln : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_ln s_singleton; @@ -1335,7 +1335,7 @@ protected: class Create_func_load_file : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_load_file s_singleton; @@ -1348,7 +1348,7 @@ protected: class Create_func_locate : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_locate s_singleton; @@ -1361,7 +1361,7 @@ protected: class Create_func_log : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_log s_singleton; @@ -1374,7 +1374,7 @@ protected: class Create_func_log10 : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_log10 s_singleton; @@ -1387,7 +1387,7 @@ protected: class Create_func_log2 : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_log2 s_singleton; @@ -1400,7 +1400,7 @@ protected: class Create_func_lpad : public Create_func_arg3 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2, Item *arg3); + virtual Item *create(THD *thd, Item *arg1, Item *arg2, Item *arg3); static Create_func_lpad s_singleton; @@ -1413,7 +1413,7 @@ protected: class Create_func_ltrim : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_ltrim s_singleton; @@ -1426,7 +1426,7 @@ protected: class Create_func_makedate : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_makedate s_singleton; @@ -1439,7 +1439,7 @@ protected: class Create_func_maketime : public Create_func_arg3 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2, Item *arg3); + virtual Item *create(THD *thd, Item *arg1, Item *arg2, Item *arg3); static Create_func_maketime s_singleton; @@ -1452,7 +1452,7 @@ protected: class Create_func_make_set : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_make_set s_singleton; @@ -1465,7 +1465,7 @@ protected: class Create_func_master_pos_wait : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_master_pos_wait s_singleton; @@ -1478,7 +1478,7 @@ protected: class Create_func_md5 : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_md5 s_singleton; @@ -1491,7 +1491,7 @@ protected: class Create_func_monthname : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_monthname s_singleton; @@ -1504,7 +1504,7 @@ protected: class Create_func_name_const : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_name_const s_singleton; @@ -1517,7 +1517,7 @@ protected: class Create_func_nullif : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_nullif s_singleton; @@ -1531,7 +1531,7 @@ protected: class Create_func_numgeometries : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_numgeometries s_singleton; @@ -1546,7 +1546,7 @@ protected: class Create_func_numinteriorring : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_numinteriorring s_singleton; @@ -1561,7 +1561,7 @@ protected: class Create_func_numpoints : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_numpoints s_singleton; @@ -1575,7 +1575,7 @@ protected: class Create_func_oct : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_oct s_singleton; @@ -1588,7 +1588,7 @@ protected: class Create_func_ord : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_ord s_singleton; @@ -1602,7 +1602,7 @@ protected: class Create_func_overlaps : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_overlaps s_singleton; @@ -1616,7 +1616,7 @@ protected: class Create_func_period_add : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_period_add s_singleton; @@ -1629,7 +1629,7 @@ protected: class Create_func_period_diff : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_period_diff s_singleton; @@ -1642,7 +1642,7 @@ protected: class Create_func_pi : public Create_func_arg0 { public: - virtual Item* create(THD *thd); + virtual Item *create(THD *thd); static Create_func_pi s_singleton; @@ -1656,7 +1656,7 @@ protected: class Create_func_pointn : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_pointn s_singleton; @@ -1670,7 +1670,7 @@ protected: class Create_func_pow : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_pow s_singleton; @@ -1683,7 +1683,7 @@ protected: class Create_func_quote : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_quote s_singleton; @@ -1696,7 +1696,7 @@ protected: class Create_func_radians : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_radians s_singleton; @@ -1709,7 +1709,7 @@ protected: class Create_func_rand : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_rand s_singleton; @@ -1722,7 +1722,7 @@ protected: class Create_func_release_lock : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_release_lock s_singleton; @@ -1735,7 +1735,7 @@ protected: class Create_func_reverse : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_reverse s_singleton; @@ -1748,7 +1748,7 @@ protected: class Create_func_round : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_round s_singleton; @@ -1761,7 +1761,7 @@ protected: class Create_func_row_count : public Create_func_arg0 { public: - virtual Item* create(THD *thd); + virtual Item *create(THD *thd); static Create_func_row_count s_singleton; @@ -1774,7 +1774,7 @@ protected: class Create_func_rpad : public Create_func_arg3 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2, Item *arg3); + virtual Item *create(THD *thd, Item *arg1, Item *arg2, Item *arg3); static Create_func_rpad s_singleton; @@ -1787,7 +1787,7 @@ protected: class Create_func_rtrim : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_rtrim s_singleton; @@ -1800,7 +1800,7 @@ protected: class Create_func_sec_to_time : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_sec_to_time s_singleton; @@ -1813,7 +1813,7 @@ protected: class Create_func_sha : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_sha s_singleton; @@ -1826,7 +1826,7 @@ protected: class Create_func_sign : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_sign s_singleton; @@ -1839,7 +1839,7 @@ protected: class Create_func_sin : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_sin s_singleton; @@ -1852,7 +1852,7 @@ protected: class Create_func_sleep : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_sleep s_singleton; @@ -1865,7 +1865,7 @@ protected: class Create_func_soundex : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_soundex s_singleton; @@ -1878,7 +1878,7 @@ protected: class Create_func_space : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_space s_singleton; @@ -1891,7 +1891,7 @@ protected: class Create_func_sqrt : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_sqrt s_singleton; @@ -1905,7 +1905,7 @@ protected: class Create_func_srid : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_srid s_singleton; @@ -1920,7 +1920,7 @@ protected: class Create_func_startpoint : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_startpoint s_singleton; @@ -1934,7 +1934,7 @@ protected: class Create_func_str_to_date : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_str_to_date s_singleton; @@ -1947,7 +1947,7 @@ protected: class Create_func_strcmp : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_strcmp s_singleton; @@ -1960,7 +1960,7 @@ protected: class Create_func_substr_index : public Create_func_arg3 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2, Item *arg3); + virtual Item *create(THD *thd, Item *arg1, Item *arg2, Item *arg3); static Create_func_substr_index s_singleton; @@ -1973,7 +1973,7 @@ protected: class Create_func_subtime : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_subtime s_singleton; @@ -1986,7 +1986,7 @@ protected: class Create_func_tan : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_tan s_singleton; @@ -1999,7 +1999,7 @@ protected: class Create_func_time_format : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_time_format s_singleton; @@ -2012,7 +2012,7 @@ protected: class Create_func_time_to_sec : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_time_to_sec s_singleton; @@ -2025,7 +2025,7 @@ protected: class Create_func_timediff : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_timediff s_singleton; @@ -2038,7 +2038,7 @@ protected: class Create_func_to_days : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_to_days s_singleton; @@ -2052,7 +2052,7 @@ protected: class Create_func_touches : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_touches s_singleton; @@ -2066,7 +2066,7 @@ protected: class Create_func_ucase : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_ucase s_singleton; @@ -2079,7 +2079,7 @@ protected: class Create_func_uncompress : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_uncompress s_singleton; @@ -2092,7 +2092,7 @@ protected: class Create_func_uncompressed_length : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_uncompressed_length s_singleton; @@ -2105,7 +2105,7 @@ protected: class Create_func_unhex : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_unhex s_singleton; @@ -2118,7 +2118,7 @@ protected: class Create_func_unix_timestamp : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_unix_timestamp s_singleton; @@ -2131,7 +2131,7 @@ protected: class Create_func_uuid : public Create_func_arg0 { public: - virtual Item* create(THD *thd); + virtual Item *create(THD *thd); static Create_func_uuid s_singleton; @@ -2157,7 +2157,7 @@ protected: class Create_func_version : public Create_func_arg0 { public: - virtual Item* create(THD *thd); + virtual Item *create(THD *thd); static Create_func_version s_singleton; @@ -2170,7 +2170,7 @@ protected: class Create_func_weekday : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_weekday s_singleton; @@ -2183,7 +2183,7 @@ protected: class Create_func_weekofyear : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_weekofyear s_singleton; @@ -2197,7 +2197,7 @@ protected: class Create_func_within : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_within s_singleton; @@ -2212,7 +2212,7 @@ protected: class Create_func_x : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_x s_singleton; @@ -2226,7 +2226,7 @@ protected: class Create_func_xml_extractvalue : public Create_func_arg2 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2); + virtual Item *create(THD *thd, Item *arg1, Item *arg2); static Create_func_xml_extractvalue s_singleton; @@ -2239,7 +2239,7 @@ protected: class Create_func_xml_update : public Create_func_arg3 { public: - virtual Item* create(THD *thd, Item *arg1, Item *arg2, Item *arg3); + virtual Item *create(THD *thd, Item *arg1, Item *arg2, Item *arg3); static Create_func_xml_update s_singleton; @@ -2253,7 +2253,7 @@ protected: class Create_func_y : public Create_func_arg1 { public: - virtual Item* create(THD *thd, Item *arg1); + virtual Item *create(THD *thd, Item *arg1); static Create_func_y s_singleton; @@ -2267,7 +2267,7 @@ protected: class Create_func_year_week : public Create_native_func { public: - virtual Item* create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); static Create_func_year_week s_singleton; @@ -2326,7 +2326,7 @@ 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)) + if (thd->lex->copy_db_to(&db.str, &db.length)) return NULL; return create(thd, db, name, false, item_list); @@ -3339,6 +3339,7 @@ Create_func_found_rows Create_func_found_rows::s_singleton; Item* Create_func_found_rows::create(THD *thd) { + thd->lex->set_stmt_unsafe(); thd->lex->safe_to_cache_query= 0; return new (thd->mem_root) Item_func_found_rows(); } @@ -4234,6 +4235,7 @@ Create_func_row_count Create_func_row_count::s_singleton; Item* Create_func_row_count::create(THD *thd) { + thd->lex->set_stmt_unsafe(); thd->lex->safe_to_cache_query= 0; return new (thd->mem_root) Item_func_row_count(); } @@ -5039,6 +5041,18 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type, my_error(ER_M_BIGGER_THAN_D, MYF(0), ""); return 0; } + if (len > DECIMAL_MAX_PRECISION) + { + my_error(ER_TOO_BIG_PRECISION, MYF(0), len, a->name, + DECIMAL_MAX_PRECISION); + return 0; + } + if (dec > DECIMAL_MAX_SCALE) + { + my_error(ER_TOO_BIG_SCALE, MYF(0), dec, a->name, + DECIMAL_MAX_SCALE); + return 0; + } res= new (thd->mem_root) Item_decimal_typecast(a, len, dec); break; } diff --git a/sql/item_create.h b/sql/item_create.h index 7ace4032515..a3ba6bd26a6 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -53,7 +53,7 @@ public: @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; + virtual Item *create(THD *thd, LEX_STRING name, List<Item> *item_list) = 0; protected: /** Constructor */ @@ -80,7 +80,7 @@ public: @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); + virtual Item *create(THD *thd, LEX_STRING name, List<Item> *item_list); /** The builder create method, for qualified functions. @@ -127,7 +127,7 @@ extern Create_qfunc * find_qualified_function_builder(THD *thd); class Create_udf_func : public Create_func { public: - virtual Item* create(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create(THD *thd, LEX_STRING name, List<Item> *item_list); /** The builder create method, for User Defined Functions. @@ -136,7 +136,7 @@ public: @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); + Item *create(THD *thd, udf_func *fct, List<Item> *item_list); /** Singleton. */ static Create_udf_func s_singleton; diff --git a/sql/item_func.cc b/sql/item_func.cc index 557f01e2c65..0c02eb01d71 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -650,16 +650,8 @@ bool Item_func_connection_id::fix_fields(THD *thd, Item **ref) { if (Item_int_func::fix_fields(thd, ref)) return TRUE; - - /* - To replicate CONNECTION_ID() properly we should use - pseudo_thread_id on slave, which contains the value of thread_id - on master. - */ - value= ((thd->slave_thread) ? - thd->variables.pseudo_thread_id : - thd->thread_id); - + thd->thread_specific_used= TRUE; + value= thd->variables.pseudo_thread_id; return FALSE; } @@ -2925,7 +2917,8 @@ udf_handler::fix_fields(THD *thd, Item_result_field *func, String *res= arguments[i]->val_str(&buffers[i]); if (arguments[i]->null_value) continue; - f_args.args[i]= (char*) res->ptr(); + f_args.args[i]= (char*) res->c_ptr(); + f_args.lengths[i]= res->length(); break; } case INT_RESULT: @@ -3750,7 +3743,7 @@ static user_var_entry *get_variable(HASH *hash, LEX_STRING &name, entry->value=0; entry->length=0; entry->update_query_id=0; - entry->collation.set(NULL, DERIVATION_IMPLICIT); + entry->collation.set(NULL, DERIVATION_IMPLICIT, 0); entry->unsigned_flag= 0; /* If we are here, we were called from a SET or a query which sets a @@ -4384,11 +4377,11 @@ int Item_func_set_user_var::save_in_field(Field *field, bool no_conversions, else if (result_type() == DECIMAL_RESULT) { my_decimal decimal_value; - my_decimal *value= entry->val_decimal(&null_value, &decimal_value); + my_decimal *val= entry->val_decimal(&null_value, &decimal_value); if (null_value) return set_field_to_null(field); field->set_notnull(); - error=field->store_decimal(value); + error=field->store_decimal(val); } else { @@ -5600,10 +5593,20 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) #endif /* ! NO_EMBEDDED_ACCESS_CHECKS */ } + if (!m_sp->m_chistics->detistic) + used_tables_cache |= RAND_TABLE_BIT; DBUG_RETURN(res); } +void Item_func_sp::update_used_tables() +{ + Item_func::update_used_tables(); + if (!m_sp->m_chistics->detistic) + used_tables_cache |= RAND_TABLE_BIT; +} + + /* uuid_short handling. diff --git a/sql/item_func.h b/sql/item_func.h index f02b35d2e80..04e3f39f9f5 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -253,7 +253,6 @@ public: void fix_num_length_and_dec(); void find_num_type(); String *str_op(String *str) { DBUG_ASSERT(0); return 0; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -311,7 +310,6 @@ public: { max_length=args[0]->max_length; unsigned_flag=0; } void print(String *str); uint decimal_precision() const { return args[0]->decimal_precision(); } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -345,7 +343,6 @@ public: void fix_length_and_dec() {}; const char *func_name() const { return "decimal_typecast"; } void print(String *); - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -442,6 +439,7 @@ public: void fix_length_and_dec(); void fix_num_length_and_dec(); uint decimal_precision() const { return args[0]->decimal_precision(); } + bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -454,6 +452,7 @@ public: my_decimal *decimal_op(my_decimal *); const char *func_name() const { return "abs"; } void fix_length_and_dec(); + bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; // A class to handle logarithmic and trigonometric functions @@ -488,7 +487,6 @@ public: Item_func_exp(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "exp"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -498,7 +496,6 @@ public: Item_func_ln(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "ln"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -509,7 +506,6 @@ public: Item_func_log(Item *a,Item *b) :Item_dec_func(a,b) {} double val_real(); const char *func_name() const { return "log"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -519,7 +515,6 @@ public: Item_func_log2(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "log2"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -529,7 +524,6 @@ public: Item_func_log10(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "log10"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -539,7 +533,6 @@ public: Item_func_sqrt(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "sqrt"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -549,7 +542,6 @@ public: Item_func_pow(Item *a,Item *b) :Item_dec_func(a,b) {} double val_real(); const char *func_name() const { return "pow"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -559,7 +551,6 @@ public: Item_func_acos(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "acos"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_asin :public Item_dec_func @@ -568,7 +559,6 @@ public: Item_func_asin(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "asin"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_atan :public Item_dec_func @@ -578,7 +568,6 @@ public: Item_func_atan(Item *a,Item *b) :Item_dec_func(a,b) {} double val_real(); const char *func_name() const { return "atan"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_cos :public Item_dec_func @@ -587,7 +576,6 @@ public: Item_func_cos(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "cos"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_sin :public Item_dec_func @@ -596,7 +584,6 @@ public: Item_func_sin(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "sin"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_tan :public Item_dec_func @@ -605,7 +592,6 @@ public: Item_func_tan(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "tan"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_integer :public Item_int_func @@ -633,6 +619,7 @@ public: longlong int_op(); double real_op(); my_decimal *decimal_op(my_decimal *); + bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -644,6 +631,7 @@ public: longlong int_op(); double real_op(); my_decimal *decimal_op(my_decimal *); + bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; /* This handles round and truncate */ @@ -684,7 +672,6 @@ public: Item_func_sign(Item *a) :Item_int_func(a) {} const char *func_name() const { return "sign"; } longlong val_int(); - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -699,7 +686,6 @@ public: const char *func_name() const { return name; } void fix_length_and_dec() { decimals= NOT_FIXED_DEC; max_length= float_length(decimals); } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -725,7 +711,6 @@ public: void fix_length_and_dec(); enum Item_result result_type () const { return cmp_type; } bool result_as_longlong() { return compare_as_dates; }; - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} uint cmp_datetimes(ulonglong *value); }; @@ -781,7 +766,6 @@ public: longlong val_int(); const char *func_name() const { return "length"; } void fix_length_and_dec() { max_length=10; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_bit_length :public Item_func_length @@ -801,7 +785,6 @@ public: longlong val_int(); const char *func_name() const { return "char_length"; } void fix_length_and_dec() { max_length=10; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_coercibility :public Item_int_func @@ -825,7 +808,6 @@ public: longlong val_int(); void fix_length_and_dec(); void print(String *str); - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -850,7 +832,6 @@ public: longlong val_int(); const char *func_name() const { return "ascii"; } void fix_length_and_dec() { max_length=3; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_ord :public Item_int_func @@ -860,7 +841,6 @@ public: Item_func_ord(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "ord"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_find_in_set :public Item_int_func @@ -874,7 +854,6 @@ public: longlong val_int(); const char *func_name() const { return "find_in_set"; } void fix_length_and_dec(); - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; /* Base class for all bit functions: '~', '|', '^', '&', '>>', '<<' */ @@ -886,7 +865,6 @@ public: Item_func_bit(Item *a) :Item_int_func(a) {} void fix_length_and_dec() { unsigned_flag= 1; } void print(String *str) { print_op(str); } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_bit_or :public Item_func_bit @@ -912,7 +890,6 @@ public: longlong val_int(); const char *func_name() const { return "bit_count"; } void fix_length_and_dec() { max_length=2; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_shift_left :public Item_func_bit @@ -1363,7 +1340,6 @@ public: 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(uchar *int_arg) {return FALSE;} }; @@ -1495,7 +1471,7 @@ public: virtual ~Item_func_sp() {} - table_map used_tables() const { return RAND_TABLE_BIT; } + void update_used_tables(); void cleanup(); diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index ca43bd6011a..7a6721f3bbd 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -25,7 +25,7 @@ #include "mysql_priv.h" #include <m_ctype.h> -#include "md5.h" +#include "my_md5.h" #include "sha1.h" #include "my_aes.h" #include <zlib.h> @@ -1049,6 +1049,23 @@ String *Item_str_conv::val_str(String *str) } +void Item_func_lcase::fix_length_and_dec() +{ + collation.set(args[0]->collation); + multiply= collation.collation->casedn_multiply; + converter= collation.collation->cset->casedn; + max_length= args[0]->max_length * multiply; +} + +void Item_func_ucase::fix_length_and_dec() +{ + collation.set(args[0]->collation); + multiply= collation.collation->caseup_multiply; + converter= collation.collation->cset->caseup; + max_length= args[0]->max_length * multiply; +} + + String *Item_func_left::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -2703,7 +2720,8 @@ void Item_func_set_collation::fix_length_and_dec() colname, args[0]->collation.collation->csname); return; } - collation.set(set_collation, DERIVATION_EXPLICIT); + collation.set(set_collation, DERIVATION_EXPLICIT, + args[0]->collation.repertoire); max_length= args[0]->max_length; } diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index c036ec490db..155f8457156 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -50,7 +50,6 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "md5"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -94,7 +93,6 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "concat"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_concat_ws :public Item_str_func @@ -116,7 +114,6 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "reverse"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -148,13 +145,11 @@ class Item_str_conv :public Item_str_func { protected: uint multiply; - size_t (*converter)(CHARSET_INFO *cs, char *src, size_t srclen, - char *dst, size_t dstlen); + my_charset_conv_case converter; String tmp_value; public: Item_str_conv(Item *item) :Item_str_func(item) {} String *val_str(String *); - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -163,13 +158,7 @@ class Item_func_lcase :public Item_str_conv public: Item_func_lcase(Item *item) :Item_str_conv(item) {} const char *func_name() const { return "lcase"; } - void fix_length_and_dec() - { - collation.set(args[0]->collation); - multiply= collation.collation->casedn_multiply; - converter= collation.collation->cset->casedn; - max_length= args[0]->max_length * multiply; - } + void fix_length_and_dec(); }; class Item_func_ucase :public Item_str_conv @@ -177,13 +166,7 @@ class Item_func_ucase :public Item_str_conv public: Item_func_ucase(Item *item) :Item_str_conv(item) {} const char *func_name() const { return "ucase"; } - void fix_length_and_dec() - { - collation.set(args[0]->collation); - multiply= collation.collation->caseup_multiply; - converter= collation.collation->cset->caseup; - max_length= args[0]->max_length * multiply; - } + void fix_length_and_dec(); }; @@ -431,6 +414,10 @@ public: } const char *func_name() const { return "user"; } const char *fully_qualified_func_name() const { return "user()"; } + int save_in_field(Field *field, bool no_conversions) + { + return save_str_value_in_field(field, &str_value); + } }; @@ -455,7 +442,6 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "soundex"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -549,7 +535,6 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "rpad"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -562,7 +547,6 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "lpad"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -577,7 +561,6 @@ public: collation.set(default_charset()); max_length= 64; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -594,7 +577,6 @@ public: decimals=0; max_length=args[0]->max_length*2*collation.collation->mbmaxlen; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_unhex :public Item_str_func @@ -614,7 +596,6 @@ public: decimals=0; max_length=(1+args[0]->max_length)/2; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -638,7 +619,6 @@ public: } void print(String *str); const char *func_name() const { return "cast_as_binary"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -678,7 +658,6 @@ public: String* val_str(String* str); const char *func_name() const { return "inet_ntoa"; } void fix_length_and_dec() { decimals = 0; max_length=3*8+7; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_quote :public Item_str_func @@ -693,7 +672,6 @@ public: collation.set(args[0]->collation); max_length= args[0]->max_length * 2 + 2; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_conv_charset :public Item_str_func @@ -794,7 +772,6 @@ public: const char *func_name() const { return "crc32"; } void fix_length_and_dec() { max_length=10; } longlong val_int(); - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_uncompressed_length : public Item_int_func diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 1d042860d73..843c6ced263 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -624,7 +624,7 @@ void Item_exists_subselect::print(String *str) } -bool Item_in_subselect::test_limit(SELECT_LEX_UNIT *unit_arg) +bool Item_in_subselect::test_limit(st_select_lex_unit *unit_arg) { if (unit_arg->fake_select_lex && unit_arg->fake_select_lex->test_limit()) @@ -2129,10 +2129,10 @@ int subselect_uniquesubquery_engine::exec() if (!table->file->inited) table->file->ha_index_init(tab->ref.key, 0); - error= table->file->index_read(table->record[0], - tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts), - HA_READ_KEY_EXACT); + error= table->file->index_read_map(table->record[0], + tab->ref.key_buff, + make_prev_keypart_map(tab->ref.key_parts), + HA_READ_KEY_EXACT); if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) error= report_error(table, error); @@ -2239,10 +2239,10 @@ int subselect_indexsubquery_engine::exec() if (!table->file->inited) table->file->ha_index_init(tab->ref.key, 1); - error= table->file->index_read(table->record[0], - tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts), - HA_READ_KEY_EXACT); + error= table->file->index_read_map(table->record[0], + tab->ref.key_buff, + make_prev_keypart_map(tab->ref.key_parts), + HA_READ_KEY_EXACT); if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) error= report_error(table, error); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 0fa46d231a9..2684cce16ac 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -2483,6 +2483,23 @@ bool Item_sum_count_distinct::setup(THD *thd) count_field_types(select_lex, tmp_table_param, list, 0); tmp_table_param->force_copy_fields= force_copy_fields; DBUG_ASSERT(table == 0); + /* + Make create_tmp_table() convert BIT columns to BIGINT. + This is needed because BIT fields store parts of their data in table's + null bits, and we don't have methods to compare two table records, which + is needed by Unique which is used when HEAP table is used. + */ + { + List_iterator_fast<Item> li(list); + Item *item; + while ((item= li++)) + { + if (item->type() == Item::FIELD_ITEM && + ((Item_field*)item)->field->type() == FIELD_TYPE_BIT) + item->marker=4; + } + } + if (!(table= create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1, 0, (select_lex->options | thd->options), @@ -3089,6 +3106,7 @@ Item_func_group_concat::Item_func_group_concat(THD *thd, original(item) { quick_group= item->quick_group; + result.set_charset(collation.collation); } diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 95ff7dfd336..300d0788f55 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -597,7 +597,7 @@ err: strmake(buff, val_begin, min(length, sizeof(buff)-1)); push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_WRONG_VALUE_FOR_TYPE, ER(ER_WRONG_VALUE_FOR_TYPE), - date_time_type, buff, "str_to_time"); + date_time_type, buff, "str_to_date"); } DBUG_RETURN(1); } @@ -962,6 +962,44 @@ enum_monotonicity_info Item_func_to_days::get_monotonicity_info() const } +longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp) +{ + DBUG_ASSERT(fixed == 1); + MYSQL_TIME ltime; + longlong res; + if (get_arg0_date(<ime, TIME_NO_ZERO_DATE)) + { + /* got NULL, leave the incl_endp intact */ + return LONGLONG_MIN; + } + res=(longlong) calc_daynr(ltime.year,ltime.month,ltime.day); + + if (args[0]->field_type() == MYSQL_TYPE_DATE) + { + // TO_DAYS() is strictly monotonic for dates, leave incl_endp intact + return res; + } + + /* + Handle the special but practically useful case of datetime values that + point to day bound ("strictly less" comparison stays intact): + + col < '2007-09-15 00:00:00' -> TO_DAYS(col) < TO_DAYS('2007-09-15') + + which is different from the general case ("strictly less" changes to + "less or equal"): + + col < '2007-09-15 12:34:56' -> TO_DAYS(col) <= TO_DAYS('2007-09-15') + */ + if (!left_endp && !(ltime.hour || ltime.minute || ltime.second || + ltime.second_part)) + ; /* do nothing */ + else + *incl_endp= TRUE; + return res; +} + + longlong Item_func_dayofyear::val_int() { DBUG_ASSERT(fixed == 1); @@ -1152,7 +1190,7 @@ longlong Item_func_year::val_int() Get information about this Item tree monotonicity SYNOPSIS - Item_func_to_days::get_monotonicity_info() + Item_func_year::get_monotonicity_info() DESCRIPTION Get information about monotonicity of the function represented by this item @@ -1171,6 +1209,37 @@ enum_monotonicity_info Item_func_year::get_monotonicity_info() const return NON_MONOTONIC; } + +longlong Item_func_year::val_int_endpoint(bool left_endp, bool *incl_endp) +{ + DBUG_ASSERT(fixed == 1); + MYSQL_TIME ltime; + if (get_arg0_date(<ime, TIME_FUZZY_DATE)) + { + /* got NULL, leave the incl_endp intact */ + return LONGLONG_MIN; + } + + /* + Handle the special but practically useful case of datetime values that + point to year bound ("strictly less" comparison stays intact) : + + col < '2007-01-01 00:00:00' -> YEAR(col) < 2007 + + which is different from the general case ("strictly less" changes to + "less or equal"): + + col < '2007-09-15 23:00:00' -> YEAR(col) <= 2007 + */ + if (!left_endp && ltime.day == 1 && ltime.month == 1 && + !(ltime.hour || ltime.minute || ltime.second || ltime.second_part)) + ; /* do nothing */ + else + *incl_endp= TRUE; + return ltime.year; +} + + longlong Item_func_unix_timestamp::val_int() { MYSQL_TIME ltime; @@ -1594,7 +1663,7 @@ int Item_func_now::save_in_field(Field *to, bool no_conversions) void Item_func_sysdate_local::store_now_in_TIME(MYSQL_TIME *now_time) { THD *thd= current_thd; - thd->variables.time_zone->gmt_sec_to_TIME(now_time, (my_time_t) time(NULL)); + thd->variables.time_zone->gmt_sec_to_TIME(now_time, (my_time_t) my_time(0)); thd->time_zone_used= 1; } @@ -1697,7 +1766,11 @@ void Item_func_date_format::fix_length_and_dec() Item *arg1= args[1]->this_item(); decimals=0; - collation.set(thd->variables.collation_connection); + CHARSET_INFO *cs= thd->variables.collation_connection; + uint32 repertoire= arg1->collation.repertoire; + if (!thd->variables.lc_time_names->is_ascii) + repertoire|= MY_REPERTOIRE_EXTENDED; + collation.set(cs, arg1->collation.derivation, repertoire); if (arg1->type() == STRING_ITEM) { // Optimize the normal case fixed_length=1; @@ -2534,11 +2607,8 @@ longlong Item_date_typecast::val_int() { DBUG_ASSERT(fixed == 1); MYSQL_TIME ltime; - if (args[0]->get_date(<ime, TIME_FUZZY_DATE)) - { - null_value= 1; + if ((null_value= args[0]->get_date(<ime, TIME_FUZZY_DATE))) return 0; - } return (longlong) (ltime.year * 10000L + ltime.month * 100 + ltime.day); } diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index bd0954e6bdb..36556269d07 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -38,7 +38,6 @@ public: { max_length=6*MY_CHARSET_BIN_MB_MAXLEN; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -53,7 +52,6 @@ public: decimals=0; max_length=6*MY_CHARSET_BIN_MB_MAXLEN; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -70,6 +68,7 @@ public: maybe_null=1; } enum_monotonicity_info get_monotonicity_info() const; + longlong val_int_endpoint(bool left_endp, bool *incl_endp); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -129,6 +128,7 @@ public: max_length=10*my_charset_bin.mbmaxlen; maybe_null=1; } + bool check_partition_func_processor(uchar *int_arg) {return TRUE;} }; @@ -224,7 +224,6 @@ public: max_length=2*MY_CHARSET_BIN_MB_MAXLEN; maybe_null=1; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_yearweek :public Item_int_func @@ -250,6 +249,7 @@ public: longlong val_int(); const char *func_name() const { return "year"; } enum_monotonicity_info get_monotonicity_info() const; + longlong val_int_endpoint(bool left_endp, bool *incl_endp); void fix_length_and_dec() { decimals=0; @@ -303,6 +303,7 @@ class Item_func_dayname :public Item_func_weekday max_length=9*MY_CHARSET_BIN_MB_MAXLEN; maybe_null=1; } + bool check_partition_func_processor(uchar *int_arg) {return TRUE;} }; @@ -319,7 +320,6 @@ public: decimals=0; max_length=10*MY_CHARSET_BIN_MB_MAXLEN; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -603,7 +603,6 @@ public: void fix_length_and_dec(); uint format_length(const String *format); bool eq(const Item *item, bool binary_cmp) const; - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -617,7 +616,6 @@ class Item_func_from_unixtime :public Item_date_func const char *func_name() const { return "from_unixtime"; } void fix_length_and_dec(); bool get_date(MYSQL_TIME *res, uint fuzzy_date); - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -676,7 +674,6 @@ public: } const char *func_name() const { return "sec_to_time"; } bool result_as_longlong() { return TRUE; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -698,7 +695,6 @@ public: bool get_date(MYSQL_TIME *res, uint fuzzy_date); bool eq(const Item *item, bool binary_cmp) const; void print(String *str); - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -753,7 +749,6 @@ public: max_length=args[0]->max_length; maybe_null= 1; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -773,7 +768,6 @@ public: String *val_str(String *a); void fix_length_and_dec(); void print(String *str); - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -884,7 +878,6 @@ public: max_length=MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; } longlong val_int(); - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -907,7 +900,6 @@ public: } void print(String *str); const char *func_name() const { return "add_time"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} double val_real() { return val_real_from_decimal(); } my_decimal *val_decimal(my_decimal *decimal_value) { @@ -949,7 +941,6 @@ public: :Item_str_timefunc(a, b ,c) {} String *val_str(String *str); const char *func_name() const { return "maketime"; } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; class Item_func_microsecond :public Item_int_func @@ -981,7 +972,6 @@ public: maybe_null=1; } void print(String *str); - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; @@ -1028,7 +1018,6 @@ public: { return tmp_table_field_from_field_type(table, 1); } - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index f8457a1ae50..15be9c97b6e 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -570,13 +570,13 @@ String *Item_nodeset_func_rootelement::val_nodeset(String *nodeset) String * Item_nodeset_func_union::val_nodeset(String *nodeset) { - uint numnodes= pxml->length() / sizeof(MY_XML_NODE); + uint num_nodes= pxml->length() / sizeof(MY_XML_NODE); String set0, *s0= args[0]->val_nodeset(&set0); String set1, *s1= args[1]->val_nodeset(&set1); String both_str; - both_str.alloc(numnodes); + both_str.alloc(num_nodes); char *both= (char*) both_str.ptr(); - bzero((void*)both, numnodes); + bzero((void*)both, num_nodes); MY_XPATH_FLT *flt; fltbeg= (MY_XPATH_FLT*) s0->ptr(); @@ -590,7 +590,7 @@ String * Item_nodeset_func_union::val_nodeset(String *nodeset) both[flt->num]= 1; nodeset->length(0); - for (uint i= 0, pos= 0; i < numnodes; i++) + for (uint i= 0, pos= 0; i < num_nodes; i++) { if (both[i]) ((XPathFilter*)nodeset)->append_element(i, pos++); @@ -2656,7 +2656,9 @@ static uint xml_parent_tag(MY_XML_NODE *items, uint nitems, uint level) RETURN Currently only MY_XML_OK */ -static int xml_enter(MY_XML_PARSER *st,const char *attr, size_t len) +extern "C" int xml_enter(MY_XML_PARSER *st,const char *attr, size_t len); + +int xml_enter(MY_XML_PARSER *st,const char *attr, size_t len) { MY_XML_USER_DATA *data= (MY_XML_USER_DATA*)st->user_data; MY_XML_NODE *nodes= (MY_XML_NODE*) data->pxml->ptr(); @@ -2687,7 +2689,9 @@ static int xml_enter(MY_XML_PARSER *st,const char *attr, size_t len) RETURN Currently only MY_XML_OK */ -static int xml_value(MY_XML_PARSER *st,const char *attr, size_t len) +extern "C" int xml_value(MY_XML_PARSER *st,const char *attr, size_t len); + +int xml_value(MY_XML_PARSER *st,const char *attr, size_t len) { MY_XML_USER_DATA *data= (MY_XML_USER_DATA*)st->user_data; MY_XML_NODE *nodes= (MY_XML_NODE*) data->pxml->ptr(); @@ -2717,7 +2721,9 @@ static int xml_value(MY_XML_PARSER *st,const char *attr, size_t len) RETURN Currently only MY_XML_OK */ -static int xml_leave(MY_XML_PARSER *st,const char *attr, size_t len) +extern "C" int xml_leave(MY_XML_PARSER *st,const char *attr, size_t len); + +int xml_leave(MY_XML_PARSER *st,const char *attr, size_t len) { MY_XML_USER_DATA *data= (MY_XML_USER_DATA*)st->user_data; DBUG_ASSERT(data->level > 0); @@ -2769,7 +2775,7 @@ String *Item_xml_str_func::parse_xml(String *raw_xml, String *parsed_xml_buf) char buf[128]; my_snprintf(buf, sizeof(buf)-1, "parse error at line %d pos %lu: %s", my_xml_error_lineno(&p) + 1, - my_xml_error_pos(&p) + 1, + (ulong) my_xml_error_pos(&p) + 1, my_xml_error_string(&p)); push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WRONG_VALUE, diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h index 650893fa7bd..278c98baf7c 100644 --- a/sql/item_xmlfunc.h +++ b/sql/item_xmlfunc.h @@ -41,7 +41,6 @@ public: Item_func_xml_extractvalue(Item *a,Item *b) :Item_xml_str_func(a,b) {} const char *func_name() const { return "extractvalue"; } String *val_str(String *); - bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; diff --git a/sql/key.cc b/sql/key.cc index fee06ec058f..e8354ed2635 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -480,6 +480,7 @@ int key_cmp(KEY_PART_INFO *key_part, const uchar *key, uint key_length) rec0 Pointer to table->record[0] first_rec Pointer to record compare with second_rec Pointer to record compare against first_rec + DESCRIPTION This method is set-up such that it can be called directly from the priority queue and it is attempted to be optimised as much as possible diff --git a/sql/lock.cc b/sql/lock.cc index 12b69edc3d3..d43ec116a28 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -60,6 +60,11 @@ - When calling UNLOCK TABLES we call mysql_unlock_tables() for all tables used in LOCK TABLES + If table_handler->external_lock(thd, locktype) fails, we call + table_handler->external_lock(thd, F_UNLCK) for each table that was locked, + excluding one that caused failure. That means handler must cleanup itself + in case external_lock() fails. + TODO: Change to use my_malloc() ONLY when using LOCK TABLES command or when we are forced to use mysql_lock_merge. @@ -69,6 +74,11 @@ TODO: #include <hash.h> #include <assert.h> +/** + @defgroup Locking Locking + @{ +*/ + extern HASH open_cache; /* flags for get_lock_data */ @@ -111,16 +121,90 @@ static void print_lock_error(int error, const char *); static int thr_lock_errno_to_mysql[]= { 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK }; +/** + Perform semantic checks for mysql_lock_tables. + @param thd The current thread + @param tables The tables to lock + @param count The number of tables to lock + @param flags Lock flags + @return 0 if all the check passed, non zero if a check failed. +*/ +int mysql_lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags) +{ + bool log_table_write_query; + uint system_count; + uint i; + + DBUG_ENTER("mysql_lock_tables_check"); + + system_count= 0; + log_table_write_query= (is_log_table_write_query(thd->lex->sql_command) + || ((flags & MYSQL_LOCK_PERF_SCHEMA) != 0)); + + for (i=0 ; i<count; i++) + { + TABLE *t= tables[i]; + + /* Protect against 'fake' partially initialized TABLE_SHARE */ + DBUG_ASSERT(t->s->table_category != TABLE_UNKNOWN_CATEGORY); + + /* + Table I/O to performance schema tables is performed + only internally by the server implementation. + When a user is requesting a lock, the following + constraints are enforced: + */ + if (t->s->require_write_privileges() && + ! log_table_write_query) + { + /* + A user should not be able to prevent writes, + or hold any type of lock in a session, + since this would be a DOS attack. + */ + if ((t->reginfo.lock_type >= TL_READ_NO_INSERT) + || (thd->lex->sql_command == SQLCOM_LOCK_TABLES)) + { + my_error(ER_CANT_LOCK_LOG_TABLE, MYF(0)); + DBUG_RETURN(1); + } + } + + if ((t->s->table_category == TABLE_CATEGORY_SYSTEM) && + (t->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE)) + { + system_count++; + } + } + + /* + Locking of system tables is restricted: + locking a mix of system and non-system tables in the same lock + is prohibited, to prevent contention. + */ + if ((system_count > 0) && (system_count < count)) + { + my_error(ER_WRONG_LOCK_OF_SYSTEM_TABLE, MYF(0)); + DBUG_RETURN(1); + } + + DBUG_RETURN(0); +} + MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags, bool *need_reopen) { MYSQL_LOCK *sql_lock; TABLE *write_lock_used; int rc; + DBUG_ENTER("mysql_lock_tables"); *need_reopen= FALSE; + if (mysql_lock_tables_check(thd, tables, count, flags)) + DBUG_RETURN (NULL); + for (;;) { if (! (sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS, @@ -170,7 +254,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, thd->proc_info="System lock"; DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info)); - if (lock_external(thd, tables, count)) + if (sql_lock->table_count && lock_external(thd, sql_lock->table, + sql_lock->table_count)) { /* Clear the lock type of all lock data to avoid reusage. */ reset_lock_data(sql_lock); @@ -191,6 +276,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, thd->lock_id)]; if (rc > 1) /* a timeout or a deadlock */ { + if (sql_lock->table_count) + VOID(unlock_external(thd, sql_lock->table, sql_lock->table_count)); my_error(rc, MYF(0)); my_free((uchar*) sql_lock,MYF(0)); sql_lock= 0; @@ -210,6 +297,10 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, } else if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH)) { + /* + Thread was killed or lock aborted. Let upper level close all + used tables and retry or give error. + */ thd->locked=0; break; } @@ -246,7 +337,7 @@ retry: } } - thd->lock_time(); + thd->set_time_after_lock(); DBUG_RETURN (sql_lock); } @@ -266,11 +357,13 @@ static int lock_external(THD *thd, TABLE **tables, uint count) ((*tables)->reginfo.lock_type >= TL_READ && (*tables)->reginfo.lock_type <= TL_READ_NO_INSERT)) lock_type=F_RDLCK; + if ((error=(*tables)->file->ha_external_lock(thd,lock_type))) { print_lock_error(error, (*tables)->file->table_type()); - for (; i-- ; tables--) + while (--i) { + tables--; (*tables)->file->ha_external_lock(thd, F_UNLCK); (*tables)->current_lock=F_UNLCK; } @@ -373,10 +466,31 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock) } +/** + Try to find the table in the list of locked tables. + In case of success, unlock the table and remove it from this list. + + @note This function has a legacy side effect: the table is + unlocked even if it is not found in the locked list. + It's not clear if this side effect is intentional or still + desirable. It might lead to unmatched calls to + unlock_external(). Moreover, a discrepancy can be left + unnoticed by the storage engine, because in + unlock_external() we call handler::external_lock(F_UNLCK) only + if table->current_lock is not F_UNLCK. + + @param thd thread context + @param locked list of locked tables + @param table the table to unlock + @param always_unlock specify explicitly if the legacy side + effect is desired. +*/ -void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table) +void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table, + bool always_unlock) { - mysql_unlock_some_tables(thd, &table,1); + if (always_unlock == TRUE) + mysql_unlock_some_tables(thd, &table, /* table count */ 1); if (locked) { reg1 uint i; @@ -390,6 +504,10 @@ void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table) DBUG_ASSERT(table->lock_position == i); + /* Unlock if not yet unlocked */ + if (always_unlock == FALSE) + mysql_unlock_some_tables(thd, &table, /* table count */ 1); + /* Decrement table_count in advance, making below expressions easier */ old_tables= --locked->table_count; @@ -439,7 +557,8 @@ void mysql_lock_downgrade_write(THD *thd, TABLE *table, { MYSQL_LOCK *locked; TABLE *write_lock_used; - if ((locked = get_lock_data(thd,&table,1,1,&write_lock_used))) + if ((locked = get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK, + &write_lock_used))) { for (uint i=0; i < locked->lock_count; i++) thr_downgrade_write_lock(locked->locks[i], new_lock_type); @@ -698,25 +817,19 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, TABLE **to, **table_buf; DBUG_ENTER("get_lock_data"); + DBUG_ASSERT((flags == GET_LOCK_UNLOCK) || (flags == GET_LOCK_STORE_LOCKS)); + DBUG_PRINT("info", ("count %d", count)); *write_lock_used=0; - uint system_count= 0; for (i=tables=lock_count=0 ; i < count ; i++) { - if (table_ptr[i]->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE) + TABLE *t= table_ptr[i]; + + if (t->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE) { - tables+=table_ptr[i]->file->lock_count(); + tables+= t->file->lock_count(); lock_count++; } - /* - Check if we can lock the table. For some tables we cannot do that - beacause of handler-specific locking issues. - */ - if (!table_ptr[i]-> file-> - check_if_locking_is_allowed(thd->lex->sql_command, thd->lex->type, - table_ptr[i], count, i, &system_count, - logger.is_privileged_thread(thd))) - DBUG_RETURN(0); } /* @@ -746,6 +859,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, if ((table=table_ptr[i])->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE) continue; lock_type= table->reginfo.lock_type; + DBUG_ASSERT (lock_type != TL_WRITE_DEFAULT); if (lock_type >= TL_WRITE_ALLOW_WRITE) { *write_lock_used=table; @@ -1033,6 +1147,104 @@ end: } +/** + @brief Lock all tables in list with an exclusive table name lock. + + @param thd Thread handle. + @param table_list Names of tables to lock. + + @note This function needs to be protected by LOCK_open. If we're + under LOCK TABLES, this function does not work as advertised. Namely, + it does not exclude other threads from using this table and does not + put an exclusive name lock on this table into the table cache. + + @see lock_table_names + @see unlock_table_names + + @retval TRUE An error occured. + @retval FALSE Name lock successfully acquired. +*/ + +bool lock_table_names_exclusively(THD *thd, TABLE_LIST *table_list) +{ + if (lock_table_names(thd, table_list)) + return TRUE; + + /* + Upgrade the table name locks from semi-exclusive to exclusive locks. + */ + for (TABLE_LIST *table= table_list; table; table= table->next_global) + { + if (table->table) + table->table->open_placeholder= 1; + } + return FALSE; +} + + +/** + @brief Test is 'table' is protected by an exclusive name lock. + + @param[in] thd The current thread handler + @param[in] table_list Table container containing the single table to be + tested + + @note Needs to be protected by LOCK_open mutex. + + @return Error status code + @retval TRUE Table is protected + @retval FALSE Table is not protected +*/ + +bool +is_table_name_exclusively_locked_by_this_thread(THD *thd, + TABLE_LIST *table_list) +{ + char key[MAX_DBKEY_LENGTH]; + uint key_length; + + key_length= create_table_def_key(thd, key, table_list, 0); + + return is_table_name_exclusively_locked_by_this_thread(thd, (uchar *)key, + key_length); +} + + +/** + @brief Test is 'table key' is protected by an exclusive name lock. + + @param[in] thd The current thread handler. + @param[in] key + @param[in] key_length + + @note Needs to be protected by LOCK_open mutex + + @retval TRUE Table is protected + @retval FALSE Table is not protected + */ + +bool +is_table_name_exclusively_locked_by_this_thread(THD *thd, uchar *key, + int key_length) +{ + HASH_SEARCH_STATE state; + TABLE *table; + + for (table= (TABLE*) hash_first(&open_cache, key, + key_length, &state); + table ; + table= (TABLE*) hash_next(&open_cache, key, + key_length, &state)) + { + if (table->in_use == thd && + table->open_placeholder == 1 && + table->s->version == 0) + return TRUE; + } + + return FALSE; +} + /* Unlock all tables in list with a name lock @@ -1386,4 +1598,6 @@ void broadcast_refresh(void) VOID(pthread_cond_broadcast(&COND_global_read_lock)); } - +/** + @} (end of group Locking) +*/ diff --git a/sql/log.cc b/sql/log.cc index 1a7ec087c51..f8c78b03228 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -57,6 +57,35 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all); static int binlog_rollback(handlerton *hton, THD *thd, bool all); static int binlog_prepare(handlerton *hton, THD *thd, bool all); +/** + Silence all errors and warnings reported when performing a write + to a log table. + Errors and warnings are not reported to the client or SQL exception + handlers, so that the presence of logging does not interfere and affect + the logic of an application. +*/ +class Silence_log_table_errors : public Internal_error_handler +{ +public: + Silence_log_table_errors() + {} + + virtual ~Silence_log_table_errors() {} + + virtual bool handle_error(uint sql_errno, + MYSQL_ERROR::enum_warning_level level, + THD *thd); +}; + +bool +Silence_log_table_errors::handle_error(uint /* sql_errno */, + MYSQL_ERROR::enum_warning_level /* level */, + THD * /* thd */) +{ + return TRUE; +} + + sql_print_message_func sql_print_message_handlers[3] = { sql_print_information, @@ -187,6 +216,19 @@ public: handlerton *binlog_hton; +bool LOGGER::is_log_table_enabled(uint log_table_type) +{ + switch (log_table_type) { + case QUERY_LOG_SLOW: + return (table_log_handler != NULL) && opt_slow_log; + case QUERY_LOG_GENERAL: + return (table_log_handler != NULL) && opt_log ; + default: + DBUG_ASSERT(0); + return FALSE; /* make compiler happy */ + } +} + /* Check if a given table is opened log table */ int check_if_log_table(uint db_len, const char *db, uint table_name_len, @@ -200,211 +242,38 @@ int check_if_log_table(uint db_len, const char *db, uint table_name_len, 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))) + strcmp(table_name, "general_log"))) + { + if (!check_if_opened || logger.is_log_table_enabled(QUERY_LOG_GENERAL)) + return QUERY_LOG_GENERAL; + return 0; + } + + if (table_name_len == 8 && !(lower_case_table_names ? + my_strcasecmp(system_charset_info, table_name, "slow_log") : + strcmp(table_name, "slow_log"))) + { + if (!check_if_opened || logger.is_log_table_enabled(QUERY_LOG_SLOW)) return QUERY_LOG_SLOW; + return 0; + } } return 0; } -/* - Open log table of a given type (general or slow log) - - SYNOPSIS - open_log_table() - - log_table_type type of the log table to open: QUERY_LOG_GENERAL - or QUERY_LOG_SLOW - - DESCRIPTION - - The function opens a log table and marks it as such. Log tables are open - during the whole time, while server is running. Except for the moments - when they have to be reopened: during FLUSH LOGS and TRUNCATE. This - function is invoked directly only once during startup. All subsequent - calls happen through reopen_log_table(), which performs additional check. - - RETURN - FALSE - OK - TRUE - error occured -*/ - -bool Log_to_csv_event_handler::open_log_table(uint log_table_type) -{ - THD *log_thd, *curr= current_thd; - TABLE_LIST *table; - bool error= FALSE; - DBUG_ENTER("open_log_table"); - - switch (log_table_type) { - case QUERY_LOG_GENERAL: - log_thd= general_log_thd; - table= &general_log; - /* clean up table before reuse/initial usage */ - bzero((char*) table, sizeof(TABLE_LIST)); - table->alias= table->table_name= (char*) "general_log"; - table->table_name_length= 11; - break; - case QUERY_LOG_SLOW: - log_thd= slow_log_thd; - table= &slow_log; - bzero((char*) table, sizeof(TABLE_LIST)); - table->alias= table->table_name= (char*) "slow_log"; - table->table_name_length= 8; - break; - default: - assert(0); // Impossible - } - - /* - This way we check that appropriate log thd was created ok during - initialization. We cannot check "is_log_tables_initialized" var, as - the very initialization is not finished until this function is - completed in the very first time. - */ - if (!log_thd) - { - DBUG_PRINT("error",("Cannot initialize log tables")); - DBUG_RETURN(TRUE); - } - - /* - Set THD's thread_stack. This is needed to perform stack overrun - check, which is done by some routines (e.g. open_table()). - In the case we are called by thread, which already has this parameter - set, we use this value. Otherwise we do a wild guess. This won't help - to correctly track the stack overrun in these exceptional cases (which - could probably happen only during startup and shutdown) but at least - lets us to pass asserts. - The problem stems from the fact that logger THDs are not real threads. - */ - if (curr) - log_thd->thread_stack= curr->thread_stack; - else - log_thd->thread_stack= (char*) &log_thd; - - log_thd->store_globals(); - - table->lock_type= TL_WRITE_CONCURRENT_INSERT; - table->db= log_thd->db; - table->db_length= log_thd->db_length; - - lex_start(log_thd); - log_thd->clear_error(); - if (simple_open_n_lock_tables(log_thd, table) || - table->table->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE) || - table->table->file->ha_rnd_init(0)) - error= TRUE; - else - { - table->table->use_all_columns(); - table->table->locked_by_logger= TRUE; - table->table->no_replicate= TRUE; - - /* Honor next number columns if present */ - table->table->next_number_field= table->table->found_next_number_field; - } - /* restore thread settings */ - if (curr) - curr->store_globals(); - else - { - my_pthread_setspecific_ptr(THR_THD, 0); - 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); -} - - Log_to_csv_event_handler::Log_to_csv_event_handler() { - /* init artificial THD's */ - general_log_thd= new THD; - /* 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; } Log_to_csv_event_handler::~Log_to_csv_event_handler() { - /* now cleanup the tables */ - if (general_log_thd) - { - delete general_log_thd; - general_log_thd= NULL; - } - - if (slow_log_thd) - { - delete slow_log_thd; - slow_log_thd= NULL; - } -} - - -/* - Reopen log table of a given type - - SYNOPSIS - reopen_log_table() - - log_table_type type of the log table to open: QUERY_LOG_GENERAL - or QUERY_LOG_SLOW - - DESCRIPTION - - The function is a wrapper around open_log_table(). It is used during - FLUSH LOGS and TRUNCATE of the log tables (i.e. when we need to close - and reopen them). The difference is in the check of the - logger.is_log_tables_initialized var, which can't be done in - open_log_table(), as it makes no sense during startup. - - NOTE: this code assumes that we have logger mutex locked - - RETURN - FALSE - ok - TRUE - open_log_table() returned an error -*/ - -bool Log_to_csv_event_handler::reopen_log_table(uint log_table_type) -{ - /* don't open the log table, if it wasn't enabled during startup */ - if (!logger.is_log_tables_initialized) - return FALSE; - return open_log_table(log_table_type); } void Log_to_csv_event_handler::cleanup() { - if (opt_log) - close_log_table(QUERY_LOG_GENERAL, FALSE); - if (opt_slow_log) - close_log_table(QUERY_LOG_SLOW, FALSE); logger.is_log_tables_initialized= FALSE; } @@ -436,49 +305,84 @@ void Log_to_csv_event_handler::cleanup() */ bool Log_to_csv_event_handler:: - log_general(time_t event_time, const char *user_host, + log_general(THD *thd, time_t event_time, const char *user_host, uint user_host_len, int thread_id, const char *command_type, uint command_type_len, const char *sql_text, uint sql_text_len, CHARSET_INFO *client_cs) { - TABLE *table= general_log.table; + TABLE_LIST table_list; + TABLE *table; + bool result= TRUE; + bool need_close= FALSE; + bool need_pop= FALSE; + bool need_rnd_end= FALSE; uint field_index; + Silence_log_table_errors error_handler; + Open_tables_state open_tables_backup; + ulonglong save_thd_options; + bool save_time_zone_used; + + /* + CSV uses TIME_to_timestamp() internally if table needs to be repaired + which will set thd->time_zone_used + */ + save_time_zone_used= thd->time_zone_used; + + save_thd_options= thd->options; + thd->options&= ~OPTION_BIN_LOG; + + bzero(& table_list, sizeof(TABLE_LIST)); + table_list.alias= table_list.table_name= GENERAL_LOG_NAME.str; + table_list.table_name_length= GENERAL_LOG_NAME.length; + + table_list.lock_type= TL_WRITE_CONCURRENT_INSERT; + + table_list.db= MYSQL_SCHEMA_NAME.str; + table_list.db_length= MYSQL_SCHEMA_NAME.length; + + if (!(table= open_performance_schema_table(thd, & table_list, + & open_tables_backup))) + goto err; + + need_close= TRUE; + + if (table->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE) || + table->file->ha_rnd_init(0)) + goto err; + + need_rnd_end= TRUE; + + /* Honor next number columns if present */ + table->next_number_field= table->found_next_number_field; /* "INSERT INTO general_log" can generate warning sometimes. - Let's reset warnings from previous queries, - otherwise warning list can grow too much, - so thd->query gets spoiled as some point in time, - and mysql_parse() receives a broken query. QQ: this problem needs to be studied in more details. - Probably it's better to suppress warnings in logging INSERTs at all. - Comment this line and run "cast.test" to see what's happening: + Comment this 2 lines and run "cast.test" to see what's happening: */ - mysql_reset_errors(table->in_use, 1); - - /* below should never happen */ - if (unlikely(!logger.is_log_tables_initialized)) - return FALSE; + thd->push_internal_handler(& error_handler); + need_pop= TRUE; /* NOTE: we do not call restore_record() here, as all fields are filled by the Logger (=> no need to load default ones). */ - /* Set current time. Required for CURRENT_TIMESTAMP to work */ - general_log_thd->start_time= event_time; - /* We do not set a value for table->field[0], as it will use default value (which is CURRENT_TIMESTAMP). */ /* check that all columns exist */ - if (!table->field[1] || !table->field[2] || !table->field[3] || - !table->field[4] || !table->field[5]) + if (table->s->fields < 6) goto err; + DBUG_ASSERT(table->field[0]->type() == MYSQL_TYPE_TIMESTAMP); + + ((Field_timestamp*) table->field[0])->store_timestamp((my_time_t) + event_time); + /* do a write */ if (table->field[1]->store(user_host, user_host_len, client_cs) || table->field[2]->store((longlong) thread_id, TRUE) || @@ -487,7 +391,8 @@ bool Log_to_csv_event_handler:: table->field[5]->store(sql_text, sql_text_len, client_cs)) goto err; - /* mark tables as not null */ + + /* mark all fields as not null */ table->field[1]->set_notnull(); table->field[2]->set_notnull(); table->field[3]->set_notnull(); @@ -500,16 +405,34 @@ bool Log_to_csv_event_handler:: table->field[field_index]->set_default(); } - /* log table entries are not replicated at the moment */ - tmp_disable_binlog(current_thd); + /* log table entries are not replicated */ + if (table->file->ha_write_row(table->record[0])) + { + struct tm start; + localtime_r(&event_time, &start); - table->file->ha_write_row(table->record[0]); + sql_print_error("%02d%02d%02d %2d:%02d:%02d - Failed to write to mysql.general_log", + start.tm_year % 100, start.tm_mon + 1, + start.tm_mday, start.tm_hour, + start.tm_min, start.tm_sec); + } - reenable_binlog(current_thd); + result= FALSE; - return FALSE; err: - return TRUE; + if (need_rnd_end) + { + table->file->ha_rnd_end(); + table->file->ha_release_auto_increment(); + } + if (need_pop) + thd->pop_internal_handler(); + if (need_close) + close_performance_schema_table(thd, & open_tables_backup); + + thd->options= save_thd_options; + thd->time_zone_used= save_time_zone_used; + return result; } @@ -524,8 +447,8 @@ err: user_host the pointer to the string with user@host info user_host_len length of the user_host string. this is computed once and passed to all general log event handlers - query_time Amount of time the query took to execute (in seconds) - lock_time Amount of time the query was locked (in seconds) + query_time Amount of time the query took to execute (in microseconds) + lock_time Amount of time the query was locked (in microseconds) is_command The flag, which determines, whether the sql_text is a query or an administrator command (these are treated differently by the old logging routines) @@ -545,43 +468,66 @@ err: bool Log_to_csv_event_handler:: log_slow(THD *thd, time_t current_time, time_t query_start_arg, const char *user_host, uint user_host_len, - longlong query_time, longlong lock_time, bool is_command, + ulonglong query_utime, ulonglong lock_utime, bool is_command, const char *sql_text, uint sql_text_len) { - /* table variables */ - TABLE *table= slow_log.table; + TABLE_LIST table_list; + TABLE *table; + bool result= TRUE; + bool need_close= FALSE; + bool need_rnd_end= FALSE; + Open_tables_state open_tables_backup; CHARSET_INFO *client_cs= thd->variables.character_set_client; - - DBUG_ENTER("log_slow"); - - /* below should never happen */ - if (unlikely(!logger.is_log_tables_initialized)) - return FALSE; + bool save_time_zone_used; + DBUG_ENTER("Log_to_csv_event_handler::log_slow"); /* - Set start time for CURRENT_TIMESTAMP to the start of the query. - This will be default value for the field[0] + CSV uses TIME_to_timestamp() internally if table needs to be repaired + which will set thd->time_zone_used */ - slow_log_thd->start_time= query_start_arg; - restore_record(table, s->default_values); // Get empty record + save_time_zone_used= thd->time_zone_used; - /* - We do not set a value for table->field[0], as it will use - default value. - */ + bzero(& table_list, sizeof(TABLE_LIST)); + table_list.alias= table_list.table_name= SLOW_LOG_NAME.str; + table_list.table_name_length= SLOW_LOG_NAME.length; + + table_list.lock_type= TL_WRITE_CONCURRENT_INSERT; + + table_list.db= MYSQL_SCHEMA_NAME.str; + table_list.db_length= MYSQL_SCHEMA_NAME.length; - 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]) + if (!(table= open_performance_schema_table(thd, & table_list, + & open_tables_backup))) + goto err; + + need_close= TRUE; + + if (table->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE) || + table->file->ha_rnd_init(0)) + goto err; + + need_rnd_end= TRUE; + + /* Honor next number columns if present */ + table->next_number_field= table->found_next_number_field; + + restore_record(table, s->default_values); // Get empty record + + /* check that all columns exist */ + if (table->s->fields < 11) goto err; - /* store the value */ + /* store the time and user values */ + DBUG_ASSERT(table->field[0]->type() == MYSQL_TYPE_TIMESTAMP); + ((Field_timestamp*) table->field[0])->store_timestamp((my_time_t) + current_time); if (table->field[1]->store(user_host, user_host_len, client_cs)) goto err; if (query_start_arg) { + longlong query_time= (longlong) (query_utime/1000000); + longlong lock_time= (longlong) (lock_utime/1000000); /* A TIME field can not hold the full longlong range; query_time or lock_time may be truncated without warning here, if greater than @@ -612,7 +558,6 @@ bool Log_to_csv_event_handler:: table->field[4]->set_null(); table->field[5]->set_null(); } - /* fill database field */ if (thd->db) { @@ -654,17 +599,72 @@ bool Log_to_csv_event_handler:: 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); + /* log table entries are not replicated */ + if (table->file->ha_write_row(table->record[0])) + { + struct tm start; + localtime_r(¤t_time, &start); - /* write the row */ - table->file->ha_write_row(table->record[0]); + sql_print_error("%02d%02d%02d %2d:%02d:%02d - Failed to write to mysql.slow_log", + start.tm_year % 100, start.tm_mon + 1, + start.tm_mday, start.tm_hour, + start.tm_min, start.tm_sec); + } - reenable_binlog(current_thd); + result= FALSE; - DBUG_RETURN(0); err: - DBUG_RETURN(1); + if (need_rnd_end) + { + table->file->ha_rnd_end(); + table->file->ha_release_auto_increment(); + } + if (need_close) + close_performance_schema_table(thd, & open_tables_backup); + thd->time_zone_used= save_time_zone_used; + DBUG_RETURN(result); +} + +int Log_to_csv_event_handler:: + activate_log(THD *thd, uint log_table_type) +{ + TABLE_LIST table_list; + TABLE *table; + int result; + Open_tables_state open_tables_backup; + + DBUG_ENTER("Log_to_csv_event_handler::activate_log"); + + bzero(& table_list, sizeof(TABLE_LIST)); + + if (log_table_type == QUERY_LOG_GENERAL) + { + table_list.alias= table_list.table_name= GENERAL_LOG_NAME.str; + table_list.table_name_length= GENERAL_LOG_NAME.length; + } + else + { + DBUG_ASSERT(log_table_type == QUERY_LOG_SLOW); + table_list.alias= table_list.table_name= SLOW_LOG_NAME.str; + table_list.table_name_length= SLOW_LOG_NAME.length; + } + + table_list.lock_type= TL_WRITE_CONCURRENT_INSERT; + + table_list.db= MYSQL_SCHEMA_NAME.str; + table_list.db_length= MYSQL_SCHEMA_NAME.length; + + table= open_performance_schema_table(thd, & table_list, + & open_tables_backup); + if (table) + { + result= 0; + close_performance_schema_table(thd, & open_tables_backup); + } + else + result= 1; + + DBUG_RETURN(result); } bool Log_to_csv_event_handler:: @@ -694,12 +694,12 @@ void Log_to_file_event_handler::init_pthread_objects() bool Log_to_file_event_handler:: log_slow(THD *thd, time_t current_time, time_t query_start_arg, const char *user_host, uint user_host_len, - longlong query_time, longlong lock_time, bool is_command, + ulonglong query_utime, ulonglong lock_utime, bool is_command, const char *sql_text, uint sql_text_len) { return mysql_slow_log.write(thd, current_time, query_start_arg, user_host, user_host_len, - query_time, lock_time, is_command, + query_utime, lock_utime, is_command, sql_text, sql_text_len); } @@ -710,7 +710,7 @@ bool Log_to_file_event_handler:: */ bool Log_to_file_event_handler:: - log_general(time_t event_time, const char *user_host, + log_general(THD *thd, time_t event_time, const char *user_host, uint user_host_len, int thread_id, const char *command_type, uint command_type_len, const char *sql_text, uint sql_text_len, @@ -774,10 +774,10 @@ bool LOGGER::error_log_print(enum loglevel level, const char *format, va_list args) { bool error= FALSE; - Log_event_handler **current_handler= error_log_handler_list; + Log_event_handler **current_handler; /* currently we don't need locking here as there is no error_log table */ - while (*current_handler) + for (current_handler= error_log_handler_list ; *current_handler ;) error= (*current_handler++)->log_error(level, format, args) || error; return error; @@ -787,7 +787,7 @@ bool LOGGER::error_log_print(enum loglevel level, const char *format, void LOGGER::cleanup_base() { DBUG_ASSERT(inited == 1); - (void) pthread_mutex_destroy(&LOCK_logger); + rwlock_destroy(&LOCK_logger); if (table_log_handler) { table_log_handler->cleanup(); @@ -806,12 +806,6 @@ void LOGGER::cleanup_end() } -void LOGGER::close_log_table(uint log_table_type, bool lock_in_use) -{ - table_log_handler->close_log_table(log_table_type, lock_in_use); -} - - /* Perform basic log initialization: create file-based log handler and init error log. @@ -833,7 +827,7 @@ void LOGGER::init_base() init_error_log(LOG_FILE); file_log_handler->init_pthread_objects(); - (void) pthread_mutex_init(&LOCK_logger, MY_MUTEX_INIT_SLOW); + my_rwlock_init(&LOCK_logger, NULL); } @@ -848,29 +842,6 @@ void LOGGER::init_log_tables() } -bool LOGGER::reopen_log_table(uint log_table_type) -{ - return table_log_handler->reopen_log_table(log_table_type); -} - -bool LOGGER::reopen_log_tables() -{ - /* - 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; -} - - -void LOGGER::tmp_close_log_tables(THD *thd) -{ - table_log_handler->tmp_close_log_tables(thd); -} - bool LOGGER::flush_logs(THD *thd) { int rc= 0; @@ -879,19 +850,11 @@ bool LOGGER::flush_logs(THD *thd) 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 + logger.lock_exclusive(); /* reopen log files */ file_log_handler->flush(); - /* reopen tables in the case they were enabled */ - if (logger.is_log_tables_initialized) - { - if (reopen_log_tables()) - rc= TRUE; - } /* end of log flush */ logger.unlock(); return rc; @@ -904,28 +867,27 @@ bool LOGGER::flush_logs(THD *thd) SYNOPSIS slow_log_print() - thd THD of the query being logged - query The query being logged - query_length The length of the query string - query_start_arg Query start timestamp + thd THD of the query being logged + query The query being logged + query_length The length of the query string + current_utime Current time in microseconds (from undefined start) RETURN - FALSE - OK - TRUE - error occured + FALSE OK + TRUE error occured */ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length, - time_t query_start_arg) + ulonglong current_utime) + { bool error= FALSE; - Log_event_handler **current_handler= slow_log_handler_list; + Log_event_handler **current_handler; bool is_command= FALSE; char user_host_buff[MAX_USER_HOST_SIZE]; - - time_t current_time; Security_context *sctx= thd->security_ctx; uint user_host_len= 0; - longlong query_time= 0, lock_time= 0; + ulonglong query_utime, lock_utime; /* Print the message to the buffer if we have slow log enabled @@ -933,13 +895,13 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length, if (*slow_log_handler_list) { - current_time= time(NULL); + time_t current_time; /* do not log slow queries from replication threads */ - if (thd->slave_thread) + if (thd->slave_thread && !opt_log_slow_slave_statements) return 0; - lock(); + lock_shared(); if (!opt_slow_log) { unlock(); @@ -947,17 +909,22 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length, } /* fill in user_host value: the format is "%s[%s] @ %s [%s]" */ - user_host_len= strxnmov(user_host_buff, MAX_USER_HOST_SIZE, - sctx->priv_user ? sctx->priv_user : "", "[", - sctx->user ? sctx->user : "", "] @ ", - sctx->host ? sctx->host : "", " [", - sctx->ip ? sctx->ip : "", "]", NullS) - - user_host_buff; - - if (query_start_arg) + user_host_len= (strxnmov(user_host_buff, MAX_USER_HOST_SIZE, + sctx->priv_user ? sctx->priv_user : "", "[", + sctx->user ? sctx->user : "", "] @ ", + sctx->host ? sctx->host : "", " [", + sctx->ip ? sctx->ip : "", "]", NullS) - + user_host_buff); + + current_time= my_time_possible_from_micro(current_utime); + if (thd->start_utime) { - query_time= (longlong) (current_time - query_start_arg); - lock_time= (longlong) (thd->time_after_lock - query_start_arg); + query_utime= (current_utime - thd->start_utime); + lock_utime= (thd->utime_after_lock - thd->start_utime); + } + else + { + query_utime= lock_utime= 0; } if (!query) @@ -967,10 +934,10 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length, query_length= command_name[thd->command].length; } - while (*current_handler) - error= (*current_handler++)->log_slow(thd, current_time, query_start_arg, + for (current_handler= slow_log_handler_list; *current_handler ;) + error= (*current_handler++)->log_slow(thd, current_time, thd->start_time, user_host_buff, user_host_len, - query_time, lock_time, is_command, + query_utime, lock_utime, is_command, query, query_length) || error; unlock(); @@ -995,7 +962,7 @@ bool LOGGER::general_log_print(THD *thd, enum enum_server_command command, Security_context *sctx= thd->security_ctx; ulong id; uint message_buff_len= 0, user_host_len= 0; - + time_t current_time; if (thd) { /* Normal thread */ if ((thd->options & OPTION_LOG_OFF) @@ -1011,14 +978,12 @@ bool LOGGER::general_log_print(THD *thd, enum enum_server_command command, else id=0; /* Log from connect handler */ - lock(); + lock_shared(); if (!opt_log) { unlock(); return 0; } - time_t current_time= time(NULL); - user_host_len= strxnmov(user_host_buff, MAX_USER_HOST_SIZE, sctx->priv_user ? sctx->priv_user : "", "[", sctx->user ? sctx->user : "", "] @ ", @@ -1033,9 +998,10 @@ bool LOGGER::general_log_print(THD *thd, enum enum_server_command command, else message_buff[0]= '\0'; + current_time= my_time(0); while (*current_handler) error+= (*current_handler++)-> - log_general(current_time, user_host_buff, + log_general(thd, current_time, user_host_buff, user_host_len, id, command_name[(uint) command].str, command_name[(uint) command].length, @@ -1122,35 +1088,51 @@ void LOGGER::init_general_log(uint general_log_printer) bool LOGGER::activate_log_handler(THD* thd, uint log_type) { - bool res= 0; - lock(); + MYSQL_QUERY_LOG *file_log; + bool res= FALSE; + lock_exclusive(); switch (log_type) { case QUERY_LOG_SLOW: if (!opt_slow_log) { - if ((res= reopen_log_table(log_type))) - goto err; - file_log_handler->get_mysql_slow_log()-> - open_slow_log(sys_var_slow_log_path.value); - init_slow_log(log_output_options); - opt_slow_log= TRUE; + file_log= file_log_handler->get_mysql_slow_log(); + + file_log->open_slow_log(sys_var_slow_log_path.value); + if (table_log_handler->activate_log(thd, QUERY_LOG_SLOW)) + { + /* Error printed by open table in activate_log() */ + res= TRUE; + file_log->close(0); + } + else + { + init_slow_log(log_output_options); + opt_slow_log= TRUE; + } } break; case QUERY_LOG_GENERAL: if (!opt_log) { - if ((res= reopen_log_table(log_type))) - goto err; - file_log_handler->get_mysql_log()-> - open_query_log(sys_var_general_log_path.value); - init_general_log(log_output_options); - opt_log= TRUE; + file_log= file_log_handler->get_mysql_log(); + + file_log->open_query_log(sys_var_general_log_path.value); + if (table_log_handler->activate_log(thd, QUERY_LOG_GENERAL)) + { + /* Error printed by open table in activate_log() */ + res= TRUE; + file_log->close(0); + } + else + { + init_general_log(log_output_options); + opt_log= TRUE; + } } break; default: DBUG_ASSERT(0); } -err: unlock(); return res; } @@ -1158,23 +1140,17 @@ err: void LOGGER::deactivate_log_handler(THD *thd, uint log_type) { - TABLE_LIST *table_list; my_bool *tmp_opt= 0; MYSQL_LOG *file_log; - THD *log_thd; switch (log_type) { case QUERY_LOG_SLOW: - table_list= &table_log_handler->slow_log; tmp_opt= &opt_slow_log; file_log= file_log_handler->get_mysql_slow_log(); - log_thd= table_log_handler->slow_log_thd; break; case QUERY_LOG_GENERAL: - table_list= &table_log_handler->general_log; tmp_opt= &opt_log; file_log= file_log_handler->get_mysql_log(); - log_thd= table_log_handler->general_log_thd; break; default: assert(0); // Impossible @@ -1183,81 +1159,16 @@ void LOGGER::deactivate_log_handler(THD *thd, uint log_type) if (!(*tmp_opt)) return; - if (is_log_tables_initialized) - lock_and_wait_for_table_name(log_thd, table_list); - lock(); - - if (is_log_tables_initialized) - { - VOID(pthread_mutex_lock(&LOCK_open)); - close_log_table(log_type, TRUE); - table_list->table= 0; - query_cache_invalidate3(log_thd, table_list, 0); - unlock_table_name(log_thd, table_list); - VOID(pthread_mutex_unlock(&LOCK_open)); - } + lock_exclusive(); file_log->close(0); *tmp_opt= FALSE; unlock(); } -/* - 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(general_log_thd, &close_general_log, 0); - } - if (opt_slow_log) - { - close_log_table(QUERY_LOG_SLOW, TRUE); - query_cache_invalidate3(general_log_thd, &close_slow_log, 0); - } - VOID(pthread_mutex_unlock(&LOCK_open)); -} - /* the parameters are unused for the log tables */ bool Log_to_csv_event_handler::init() { - /* - we use | and not || here, to ensure that both open_log_table - are called, even if the first one fails - */ - if ((opt_log && open_log_table(QUERY_LOG_GENERAL)) | - (opt_slow_log && open_log_table(QUERY_LOG_SLOW))) - return 1; return 0; } @@ -1268,7 +1179,7 @@ int LOGGER::set_handlers(uint error_log_printer, /* error log table is not supported yet */ DBUG_ASSERT(error_log_printer < LOG_TABLE); - lock(); + lock_exclusive(); if ((slow_log_printer & LOG_TABLE || general_log_printer & LOG_TABLE) && !is_log_tables_initialized) @@ -1290,72 +1201,6 @@ int LOGGER::set_handlers(uint error_log_printer, } -/* - Close log table of a given type (general or slow log) - - SYNOPSIS - close_log_table() - - log_table_type type of the log table to close: QUERY_LOG_GENERAL - or QUERY_LOG_SLOW - lock_in_use Set to TRUE if the caller owns LOCK_open. FALSE otherwise. - - DESCRIPTION - - The function closes a log table. It is invoked (1) when we need to reopen - log tables (e.g. FLUSH LOGS or TRUNCATE on the log table is being - executed) or (2) during shutdown. -*/ - -void Log_to_csv_event_handler:: - close_log_table(uint log_table_type, bool lock_in_use) -{ - 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: - log_thd= general_log_thd; - table= &general_log; - break; - case QUERY_LOG_SLOW: - log_thd= slow_log_thd; - table= &slow_log; - break; - default: - assert(0); // Impossible - } - - /* - Set thread stack start for the logger thread. See comment in - open_log_table() for details. - */ - if (curr) - log_thd->thread_stack= curr->thread_stack; - else - log_thd->thread_stack= (char*) &log_thd; - - /* close the table */ - log_thd->store_globals(); - table->table->file->ha_rnd_end(); - table->table->file->ha_release_auto_increment(); - /* discard logger mark before unlock*/ - table->table->locked_by_logger= FALSE; - close_thread_tables(log_thd, lock_in_use); - - if (curr) - curr->store_globals(); - else - { - my_pthread_setspecific_ptr(THR_THD, 0); - my_pthread_setspecific_ptr(THR_MALLOC, 0); - } -} - - /* Save position of binary log transaction cache. @@ -1376,10 +1221,10 @@ binlog_trans_log_savepos(THD *thd, my_off_t *pos) { DBUG_ENTER("binlog_trans_log_savepos"); DBUG_ASSERT(pos != NULL); - if (thd->ha_data[binlog_hton->slot] == NULL) + if (thd_get_ha_data(thd, binlog_hton) == NULL) thd->binlog_setup_trx_data(); binlog_trx_data *const trx_data= - (binlog_trx_data*) thd->ha_data[binlog_hton->slot]; + (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton); DBUG_ASSERT(mysql_bin_log.is_open()); *pos= trx_data->position(); DBUG_PRINT("return", ("*pos: %lu", (ulong) *pos)); @@ -1408,12 +1253,12 @@ binlog_trans_log_truncate(THD *thd, my_off_t pos) DBUG_ENTER("binlog_trans_log_truncate"); DBUG_PRINT("enter", ("pos: %lu", (ulong) pos)); - DBUG_ASSERT(thd->ha_data[binlog_hton->slot] != NULL); + DBUG_ASSERT(thd_get_ha_data(thd, binlog_hton) != NULL); /* Only true if binlog_trans_log_savepos() wasn't called before */ DBUG_ASSERT(pos != ~(my_off_t) 0); binlog_trx_data *const trx_data= - (binlog_trx_data*) thd->ha_data[binlog_hton->slot]; + (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton); trx_data->truncate(pos); DBUG_VOID_RETURN; } @@ -1444,9 +1289,9 @@ int binlog_init(void *p) static int binlog_close_connection(handlerton *hton, THD *thd) { binlog_trx_data *const trx_data= - (binlog_trx_data*) thd->ha_data[binlog_hton->slot]; - DBUG_ASSERT(mysql_bin_log.is_open() && trx_data->empty()); - thd->ha_data[binlog_hton->slot]= 0; + (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton); + DBUG_ASSERT(trx_data->empty()); + thd_set_ha_data(thd, binlog_hton, NULL); trx_data->~binlog_trx_data(); my_free((uchar*)trx_data, MYF(0)); return 0; @@ -1569,8 +1414,7 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all) { DBUG_ENTER("binlog_commit"); binlog_trx_data *const trx_data= - (binlog_trx_data*) thd->ha_data[binlog_hton->slot]; - DBUG_ASSERT(mysql_bin_log.is_open()); + (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton); if (trx_data->empty()) { @@ -1597,8 +1441,7 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all) DBUG_ENTER("binlog_rollback"); int error=0; binlog_trx_data *const trx_data= - (binlog_trx_data*) thd->ha_data[binlog_hton->slot]; - DBUG_ASSERT(mysql_bin_log.is_open()); + (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton); if (trx_data->empty()) { trx_data->reset(); @@ -1611,7 +1454,8 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all) table. Such cases should be rare (updating a non-transactional table inside a transaction...) */ - if (unlikely(thd->no_trans_update.all || (thd->options & OPTION_KEEP_LOG))) + if (unlikely(thd->transaction.all.modified_non_trans_table || + (thd->options & OPTION_KEEP_LOG))) { Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, FALSE); qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE) @@ -1659,14 +1503,14 @@ 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"); - DBUG_ASSERT(mysql_bin_log.is_open()); /* Write ROLLBACK TO SAVEPOINT to the binlog cache if we have updated some non-transactional table. Otherwise, truncate the binlog cache starting from the SAVEPOINT command. */ - if (unlikely(thd->no_trans_update.all || (thd->options & OPTION_KEEP_LOG))) + if (unlikely(thd->transaction.all.modified_non_trans_table || + (thd->options & OPTION_KEEP_LOG))) { int error= thd->binlog_query(THD::STMT_QUERY_TYPE, @@ -2096,10 +1940,12 @@ bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host, { char buff[32]; uint length= 0; - char time_buff[MAX_TIME_SIZE]; + char local_time_buff[MAX_TIME_SIZE]; struct tm start; uint time_buff_len= 0; + (void) pthread_mutex_lock(&LOCK_log); + /* Test if someone closed between the is_open test and lock */ if (is_open()) { @@ -2110,13 +1956,13 @@ bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host, localtime_r(&event_time, &start); - time_buff_len= my_snprintf(time_buff, MAX_TIME_SIZE, + time_buff_len= my_snprintf(local_time_buff, MAX_TIME_SIZE, "%02d%02d%02d %2d:%02d:%02d", start.tm_year % 100, start.tm_mon + 1, start.tm_mday, start.tm_hour, start.tm_min, start.tm_sec); - if (my_b_write(&log_file, (uchar*) &time_buff, time_buff_len)) + if (my_b_write(&log_file, (uchar*) local_time_buff, time_buff_len)) goto err; } else @@ -2144,6 +1990,7 @@ bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host, goto err; } + (void) pthread_mutex_unlock(&LOCK_log); return FALSE; err: @@ -2152,6 +1999,7 @@ err: write_error= 1; sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); } + (void) pthread_mutex_unlock(&LOCK_log); return TRUE; } @@ -2168,8 +2016,8 @@ err: user_host the pointer to the string with user@host info user_host_len length of the user_host string. this is computed once and passed to all general log event handlers - query_time Amount of time the query took to execute (in seconds) - lock_time Amount of time the query was locked (in seconds) + query_utime Amount of time the query took to execute (in microseconds) + lock_utime Amount of time the query was locked (in microseconds) is_command The flag, which determines, whether the sql_text is a query or an administrator command. sql_text the very text of the query or administrator command @@ -2187,20 +2035,26 @@ err: bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, time_t query_start_arg, const char *user_host, - uint user_host_len, longlong query_time, - longlong lock_time, bool is_command, + uint user_host_len, ulonglong query_utime, + ulonglong lock_utime, bool is_command, const char *sql_text, uint sql_text_len) { bool error= 0; DBUG_ENTER("MYSQL_QUERY_LOG::write"); + (void) pthread_mutex_lock(&LOCK_log); + if (!is_open()) + { + (void) pthread_mutex_unlock(&LOCK_log); DBUG_RETURN(0); + } if (is_open()) { // Safety agains reopen int tmp_errno= 0; char buff[80], *end; + char query_time_buff[22+7], lock_time_buff[22+7]; uint buff_len; end= buff; @@ -2231,10 +2085,12 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, tmp_errno= errno; } /* For slow query log */ + sprintf(query_time_buff, "%.6f", ulonglong2double(query_utime)/1000000.0); + sprintf(lock_time_buff, "%.6f", ulonglong2double(lock_utime)/1000000.0); if (my_b_printf(&log_file, - "# Query_time: %lu Lock_time: %lu" + "# Query_time: %s Lock_time: %s" " Rows_sent: %lu Rows_examined: %lu\n", - (ulong) query_time, (ulong) lock_time, + query_time_buff, lock_time_buff, (ulong) thd->sent_row_count, (ulong) thd->examined_row_count) == (uint) -1) tmp_errno= errno; @@ -2299,6 +2155,7 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, } } } + (void) pthread_mutex_unlock(&LOCK_log); DBUG_RETURN(error); } @@ -2500,8 +2357,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name, s.flags|= LOG_EVENT_BINLOG_IN_USE_F; if (!s.is_valid()) goto err; - if (null_created_arg) - s.created= 0; + s.dont_set_created= null_created_arg; if (s.write(&log_file)) goto err; bytes_written+= s.data_written; @@ -2888,7 +2744,7 @@ err: #ifdef HAVE_REPLICATION -int MYSQL_BIN_LOG::purge_first_log(struct st_relay_log_info* rli, bool included) +int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included) { int error; DBUG_ENTER("purge_first_log"); @@ -3406,23 +3262,22 @@ int THD::binlog_setup_trx_data() { DBUG_ENTER("THD::binlog_setup_trx_data"); binlog_trx_data *trx_data= - (binlog_trx_data*) ha_data[binlog_hton->slot]; + (binlog_trx_data*) thd_get_ha_data(this, binlog_hton); if (trx_data) DBUG_RETURN(0); // Already set up - ha_data[binlog_hton->slot]= trx_data= - (binlog_trx_data*) my_malloc(sizeof(binlog_trx_data), MYF(MY_ZEROFILL)); + trx_data= (binlog_trx_data*) my_malloc(sizeof(binlog_trx_data), MYF(MY_ZEROFILL)); if (!trx_data || open_cached_file(&trx_data->trans_log, mysql_tmpdir, LOG_PREFIX, binlog_cache_size, MYF(MY_WME))) { my_free((uchar*)trx_data, MYF(MY_ALLOW_ZERO_PTR)); - ha_data[binlog_hton->slot]= 0; DBUG_RETURN(1); // Didn't manage to set it up } + thd_set_ha_data(this, binlog_hton, trx_data); - trx_data= new (ha_data[binlog_hton->slot]) binlog_trx_data; + trx_data= new (thd_get_ha_data(this, binlog_hton)) binlog_trx_data; DBUG_RETURN(0); } @@ -3458,7 +3313,7 @@ int THD::binlog_setup_trx_data() void THD::binlog_start_trans_and_stmt() { - binlog_trx_data *trx_data= (binlog_trx_data*) ha_data[binlog_hton->slot]; + binlog_trx_data *trx_data= (binlog_trx_data*) thd_get_ha_data(this, binlog_hton); DBUG_ENTER("binlog_start_trans_and_stmt"); DBUG_PRINT("enter", ("trx_data: 0x%lx trx_data->before_stmt_pos: %lu", (long) trx_data, @@ -3478,7 +3333,7 @@ THD::binlog_start_trans_and_stmt() void THD::binlog_set_stmt_begin() { binlog_trx_data *trx_data= - (binlog_trx_data*) ha_data[binlog_hton->slot]; + (binlog_trx_data*) thd_get_ha_data(this, binlog_hton); /* The call to binlog_trans_log_savepos() might create the trx_data @@ -3488,14 +3343,15 @@ void THD::binlog_set_stmt_begin() { */ my_off_t pos= 0; binlog_trans_log_savepos(this, &pos); - trx_data= (binlog_trx_data*) ha_data[binlog_hton->slot]; + trx_data= (binlog_trx_data*) thd_get_ha_data(this, binlog_hton); 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]; + binlog_trx_data *trx_data= (binlog_trx_data*) + thd_get_ha_data(this, binlog_hton); DBUG_PRINT("enter", ("trx_data=0x%lu", (ulong) trx_data)); if (trx_data) DBUG_PRINT("enter", ("trx_data->before_stmt_pos=%lu", @@ -3558,7 +3414,7 @@ Rows_log_event* THD::binlog_get_pending_rows_event() const { binlog_trx_data *const trx_data= - (binlog_trx_data*) ha_data[binlog_hton->slot]; + (binlog_trx_data*) thd_get_ha_data(this, binlog_hton); /* This is less than ideal, but here's the story: If there is no trx_data, prepare_pending_rows_event() has never been called @@ -3571,11 +3427,11 @@ THD::binlog_get_pending_rows_event() const void THD::binlog_set_pending_rows_event(Rows_log_event* ev) { - if (ha_data[binlog_hton->slot] == NULL) + if (thd_get_ha_data(this, binlog_hton) == NULL) binlog_setup_trx_data(); binlog_trx_data *const trx_data= - (binlog_trx_data*) ha_data[binlog_hton->slot]; + (binlog_trx_data*) thd_get_ha_data(this, binlog_hton); DBUG_ASSERT(trx_data); trx_data->set_pending(ev); @@ -3598,7 +3454,7 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd, int error= 0; binlog_trx_data *const trx_data= - (binlog_trx_data*) thd->ha_data[binlog_hton->slot]; + (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton); DBUG_ASSERT(trx_data); @@ -3749,7 +3605,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) goto err; binlog_trx_data *const trx_data= - (binlog_trx_data*) thd->ha_data[binlog_hton->slot]; + (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton); IO_CACHE *trans_log= &trx_data->trans_log; my_off_t trans_log_pos= my_b_tell(trans_log); if (event_info->get_cache_stmt() || trans_log_pos != 0) @@ -3880,9 +3736,9 @@ int error_log_print(enum loglevel level, const char *format, bool slow_log_print(THD *thd, const char *query, uint query_length, - time_t query_start_arg) + ulonglong current_utime) { - return logger.slow_log_print(thd, query, query_length, query_start_arg); + return logger.slow_log_print(thd, query, query_length, current_utime); } @@ -3910,7 +3766,7 @@ void MYSQL_BIN_LOG::rotate_and_purge(uint flags) #ifdef HAVE_REPLICATION if (expire_logs_days) { - time_t purge_time= time(0) - expire_logs_days*24*60*60; + time_t purge_time= my_time(0) - expire_logs_days*24*60*60; if (purge_time >= 0) purge_logs_before_date(purge_time); } @@ -3950,13 +3806,120 @@ int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log) if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0)) return ER_ERROR_ON_WRITE; - uint bytes= my_b_bytes_in_cache(cache); + uint length= my_b_bytes_in_cache(cache), group, carry, hdr_offs; + long val; + uchar header[LOG_EVENT_HEADER_LEN]; + + /* + The events in the buffer have incorrect end_log_pos data + (relative to beginning of group rather than absolute), + so we'll recalculate them in situ so the binlog is always + correct, even in the middle of a group. This is possible + because we now know the start position of the group (the + offset of this cache in the log, if you will); all we need + to do is to find all event-headers, and add the position of + the group to the end_log_pos of each event. This is pretty + straight forward, except that we read the cache in segments, + so an event-header might end up on the cache-border and get + split. + */ + + group= (uint)my_b_tell(&log_file); + hdr_offs= carry= 0; + do { - if (my_b_write(&log_file, cache->read_pos, bytes)) + + /* + if we only got a partial header in the last iteration, + get the other half now and process a full header. + */ + if (unlikely(carry > 0)) + { + DBUG_ASSERT(carry < LOG_EVENT_HEADER_LEN); + + /* assemble both halves */ + memcpy(&header[carry], (char *)cache->read_pos, LOG_EVENT_HEADER_LEN - carry); + + /* fix end_log_pos */ + val= uint4korr(&header[LOG_POS_OFFSET]) + group; + int4store(&header[LOG_POS_OFFSET], val); + + /* write the first half of the split header */ + if (my_b_write(&log_file, header, carry)) + return ER_ERROR_ON_WRITE; + + /* + copy fixed second half of header to cache so the correct + version will be written later. + */ + memcpy((char *)cache->read_pos, &header[carry], LOG_EVENT_HEADER_LEN - carry); + + /* next event header at ... */ + hdr_offs = uint4korr(&header[EVENT_LEN_OFFSET]) - carry; + + carry= 0; + } + + /* if there is anything to write, process it. */ + + if (likely(length > 0)) + { + /* + process all event-headers in this (partial) cache. + if next header is beyond current read-buffer, + we'll get it later (though not necessarily in the + very next iteration, just "eventually"). + */ + + while (hdr_offs < length) + { + /* + partial header only? save what we can get, process once + we get the rest. + */ + + if (hdr_offs + LOG_EVENT_HEADER_LEN > length) + { + carry= length - hdr_offs; + memcpy(header, (char *)cache->read_pos + hdr_offs, carry); + length= hdr_offs; + } + else + { + /* we've got a full event-header, and it came in one piece */ + + uchar *log_pos= (uchar *)cache->read_pos + hdr_offs + LOG_POS_OFFSET; + + /* fix end_log_pos */ + val= uint4korr(log_pos) + group; + int4store(log_pos, val); + + /* next event header at ... */ + log_pos= (uchar *)cache->read_pos + hdr_offs + EVENT_LEN_OFFSET; + hdr_offs += uint4korr(log_pos); + + } + } + + /* + Adjust hdr_offs. Note that it may still point beyond the segment + read in the next iteration; if the current event is very long, + it may take a couple of read-iterations (and subsequent adjustments + of hdr_offs) for it to point into the then-current segment. + If we have a split header (!carry), hdr_offs will be set at the + beginning of the next iteration, overwriting the value we set here: + */ + hdr_offs -= length; + } + + /* Write data to the binary log file */ + if (my_b_write(&log_file, cache->read_pos, length)) return ER_ERROR_ON_WRITE; - cache->read_pos= cache->read_end; - } while ((bytes= my_b_fill(cache))); + cache->read_pos=cache->read_end; // Mark buffer used up + } while ((length= my_b_fill(cache))); + + DBUG_ASSERT(carry == 0); if (sync_log) flush_and_sync(); @@ -3995,6 +3958,7 @@ bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event) /* NULL would represent nothing to replicate after ROLLBACK */ DBUG_ASSERT(commit_event != NULL); + DBUG_ASSERT(is_open()); if (likely(is_open())) // Should always be true { /* @@ -4033,7 +3997,7 @@ bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event) if ((write_error= write_cache(cache, false, false))) goto err; - + if (commit_event && commit_event->write(&log_file)) goto err; if (flush_and_sync()) @@ -4400,7 +4364,7 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer) VOID(pthread_mutex_lock(&LOCK_error_log)); - skr=time(NULL); + skr= my_time(0); localtime_r(&skr, &tm_tmp); start=&tm_tmp; @@ -5078,7 +5042,7 @@ int TC_LOG_BINLOG::log_xid(THD *thd, my_xid xid) DBUG_ENTER("TC_LOG_BINLOG::log"); Xid_log_event xle(thd, xid); binlog_trx_data *trx_data= - (binlog_trx_data*) thd->ha_data[binlog_hton->slot]; + (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton); /* We always commit the entire transaction when writing an XID. Also note that the return value is inverted. @@ -5145,6 +5109,29 @@ err1: return 1; } + +#ifdef INNODB_COMPATIBILITY_HOOKS +/** + Get the file name of the MySQL binlog. + @return the name of the binlog file +*/ +extern "C" +const char* mysql_bin_log_file_name(void) +{ + return mysql_bin_log.get_log_fname(); +} +/** + Get the current position of the MySQL binlog. + @return byte offset from the beginning of the binlog +*/ +extern "C" +ulonglong mysql_bin_log_file_pos(void) +{ + return (ulonglong) mysql_bin_log.get_log_file()->pos_in_file; +} +#endif /* INNODB_COMPATIBILITY_HOOKS */ + + struct st_mysql_storage_engine binlog_storage_engine= { MYSQL_HANDLERTON_INTERFACE_VERSION }; diff --git a/sql/log.h b/sql/log.h index d92e0117bcc..e597c986794 100644 --- a/sql/log.h +++ b/sql/log.h @@ -16,7 +16,7 @@ #ifndef LOG_H #define LOG_H -struct st_relay_log_info; +class Relay_log_info; class Format_description_log_event; @@ -121,7 +121,7 @@ extern TC_LOG_DUMMY tc_log_dummy; #define LOG_CLOSE_TO_BE_OPENED 2 #define LOG_CLOSE_STOP_EVENT 4 -struct st_relay_log_info; +class Relay_log_info; typedef struct st_log_info { @@ -198,7 +198,7 @@ public: const char *sql_text, uint sql_text_len); bool write(THD *thd, time_t current_time, time_t query_start_arg, const char *user_host, uint user_host_len, - longlong query_time, longlong lock_time, bool is_command, + ulonglong query_utime, ulonglong lock_utime, bool is_command, const char *sql_text, uint sql_text_len); bool open_slow_log(const char *log_name) { @@ -212,6 +212,7 @@ public: return open(generate_name(log_name, ".log", 0, buf), LOG_NORMAL, 0, WRITE_CACHE); } + private: time_t last_time; }; @@ -361,7 +362,7 @@ public: bool need_mutex, bool need_update_threads, ulonglong *decrease_log_space); int purge_logs_before_date(time_t purge_time); - int purge_first_log(struct st_relay_log_info* rli, bool included); + int purge_first_log(Relay_log_info* rli, bool included); bool reset_logs(THD* thd); void close(uint exiting); @@ -393,12 +394,12 @@ public: virtual bool log_slow(THD *thd, time_t current_time, time_t query_start_arg, const char *user_host, - uint user_host_len, longlong query_time, - longlong lock_time, bool is_command, + uint user_host_len, ulonglong query_utime, + ulonglong lock_utime, bool is_command, const char *sql_text, uint sql_text_len)= 0; virtual bool log_error(enum loglevel level, const char *format, va_list args)= 0; - virtual bool log_general(time_t event_time, const char *user_host, + virtual bool log_general(THD *thd, time_t event_time, const char *user_host, uint user_host_len, int thread_id, const char *command_type, uint command_type_len, const char *sql_text, uint sql_text_len, @@ -412,27 +413,7 @@ int check_if_log_table(uint db_len, const char *db, uint table_name_len, class Log_to_csv_event_handler: public Log_event_handler { - /* - We create artificial THD for each of the logs. This is to avoid - locking issues: we don't want locks on the log tables reside in the - 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; - -private: - bool open_log_table(uint log_type); public: Log_to_csv_event_handler(); @@ -442,23 +423,18 @@ public: virtual bool log_slow(THD *thd, time_t current_time, time_t query_start_arg, const char *user_host, - uint user_host_len, longlong query_time, - longlong lock_time, bool is_command, + uint user_host_len, ulonglong query_utime, + ulonglong lock_utime, bool is_command, const char *sql_text, uint sql_text_len); virtual bool log_error(enum loglevel level, const char *format, va_list args); - virtual bool log_general(time_t event_time, const char *user_host, + virtual bool log_general(THD *thd, time_t event_time, const char *user_host, uint user_host_len, int thread_id, const char *command_type, uint command_type_len, const char *sql_text, uint sql_text_len, - CHARSET_INFO *client_cs); - 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; - } + CHARSET_INFO *client_cs); + + int activate_log(THD *thd, uint log_type); }; @@ -479,12 +455,12 @@ public: virtual bool log_slow(THD *thd, time_t current_time, time_t query_start_arg, const char *user_host, - uint user_host_len, longlong query_time, - longlong lock_time, bool is_command, + uint user_host_len, ulonglong query_utime, + ulonglong lock_utime, bool is_command, const char *sql_text, uint sql_text_len); virtual bool log_error(enum loglevel level, const char *format, va_list args); - virtual bool log_general(time_t event_time, const char *user_host, + virtual bool log_general(THD *thd, time_t event_time, const char *user_host, uint user_host_len, int thread_id, const char *command_type, uint command_type_len, const char *sql_text, uint sql_text_len, @@ -499,7 +475,7 @@ public: /* Class which manages slow, general and error log event handlers */ class LOGGER { - pthread_mutex_t LOCK_logger; + rw_lock_t LOCK_logger; /* flag to check whether logger mutex is initialized */ uint inited; @@ -519,21 +495,10 @@ public: LOGGER() : inited(0), table_log_handler(NULL), file_log_handler(NULL), is_log_tables_initialized(FALSE) {} - void lock() { (void) pthread_mutex_lock(&LOCK_logger); } - void unlock() { (void) pthread_mutex_unlock(&LOCK_logger); } - void tmp_close_log_tables(THD *thd); - bool is_log_table_enabled(uint log_table_type) - { - 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 */ - } - } + void lock_shared() { rw_rdlock(&LOCK_logger); } + void lock_exclusive() { rw_wrlock(&LOCK_logger); } + void unlock() { rw_unlock(&LOCK_logger); } + bool is_log_table_enabled(uint log_table_type); /* We want to initialize all log mutexes as soon as possible, but we cannot do it in constructor, as safe_mutex relies on @@ -543,20 +508,6 @@ public: void init_base(); void init_log_tables(); bool flush_logs(THD *thd); - THD *get_general_log_thd() - { - if (table_log_handler) - return (THD *) table_log_handler->general_log_thd; - else - return NULL; - } - THD *get_slow_log_thd() - { - if (table_log_handler) - return (THD *) table_log_handler->slow_log_thd; - else - return NULL; - } /* Perform basic logger cleanup. this will leave e.g. error log open. */ void cleanup_base(); /* Free memory. Nothing could be logged after this function is called */ @@ -564,14 +515,10 @@ public: bool error_log_print(enum loglevel level, const char *format, va_list args); bool slow_log_print(THD *thd, const char *query, uint query_length, - time_t query_start_arg); + ulonglong current_utime); bool general_log_print(THD *thd,enum enum_server_command command, const char *format, va_list args); - 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, uint slow_log_printer, @@ -593,19 +540,6 @@ 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; - } - bool is_privileged_thread(THD *thd) - { - return thd == get_general_log_thd() || - thd == get_slow_log_thd() || - thd == get_privileged_thread(); - } }; enum enum_binlog_format { diff --git a/sql/log_event.cc b/sql/log_event.cc index 02d3a949cb3..d8e22953fc2 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -146,7 +146,7 @@ static void pretty_print_str(IO_CACHE* cache, char* str, int len) #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) -static void clear_all_errors(THD *thd, RELAY_LOG_INFO *rli) +static void clear_all_errors(THD *thd, Relay_log_info *rli) { thd->query_error = 0; thd->clear_error(); @@ -456,7 +456,11 @@ Log_event::Log_event() thd(0) { server_id= ::server_id; - when= time(NULL); + /* + We can't call my_time() here as this would cause a call before + my_init() is called + */ + when= 0; log_pos= 0; } #endif /* !MYSQL_CLIENT */ @@ -536,7 +540,7 @@ Log_event::Log_event(const char* buf, #ifndef MYSQL_CLIENT #ifdef HAVE_REPLICATION -int Log_event::do_update_pos(RELAY_LOG_INFO *rli) +int Log_event::do_update_pos(Relay_log_info *rli) { /* rli is null when (as far as I (Guilhem) know) the caller is @@ -558,7 +562,7 @@ int Log_event::do_update_pos(RELAY_LOG_INFO *rli) Log_event::enum_skip_reason -Log_event::do_shall_skip(RELAY_LOG_INFO *rli) +Log_event::do_shall_skip(Relay_log_info *rli) { DBUG_PRINT("info", ("ev->server_id=%lu, ::server_id=%lu," " rli->replicate_same_server_id=%d," @@ -597,7 +601,7 @@ int Log_event::net_send(Protocol *protocol, const char* log_name, my_off_t pos) const char *event_type; if (p) log_name = p + 1; - + protocol->prepare_for_resend(); protocol->store(log_name, &my_charset_bin); protocol->store((ulonglong) pos); @@ -637,6 +641,7 @@ void Log_event::init_show_field_list(List<Item>* field_list) bool Log_event::write_header(IO_CACHE* file, ulong event_data_length) { uchar header[LOG_EVENT_HEADER_LEN]; + ulong now; DBUG_ENTER("Log_event::write_header"); /* Store number of bytes that will be written by this event */ @@ -687,6 +692,8 @@ bool Log_event::write_header(IO_CACHE* file, ulong event_data_length) log_pos= my_b_safe_tell(file)+data_written; } + now= (ulong) get_time(); // Query start time + /* Header will be of size LOG_EVENT_HEADER_LEN for all events, except for FORMAT_DESCRIPTION_EVENT and ROTATE_EVENT, where it will be @@ -694,7 +701,7 @@ bool Log_event::write_header(IO_CACHE* file, ulong event_data_length) because we read them before knowing the format). */ - int4store(header, (ulong) when); // timestamp + int4store(header, now); // timestamp header[EVENT_TYPE_OFFSET]= get_type_code(); int4store(header+ SERVER_ID_OFFSET, server_id); int4store(header+ EVENT_LEN_OFFSET, data_written); @@ -803,10 +810,12 @@ end: #ifndef MYSQL_CLIENT Log_event* Log_event::read_log_event(IO_CACHE* file, pthread_mutex_t* log_lock, - const Format_description_log_event *description_event) + const Format_description_log_event + *description_event) #else Log_event* Log_event::read_log_event(IO_CACHE* file, - const Format_description_log_event *description_event) + const Format_description_log_event + *description_event) #endif { DBUG_ENTER("Log_event::read_log_event"); @@ -1461,7 +1470,7 @@ Query_log_event::Query_log_event() /* SYNOPSIS Query_log_event::Query_log_event() - thd - thread handle + thd_arg - thread handle query_arg - array of char representing the query query_length - size of the `query_arg' array using_trans - there is a modified transactional table @@ -1477,10 +1486,12 @@ Query_log_event::Query_log_event() */ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, ulong query_length, bool using_trans, - bool suppress_use, THD::killed_state killed_status_arg) + bool suppress_use, + THD::killed_state killed_status_arg) :Log_event(thd_arg, - ((thd_arg->tmp_table_used ? LOG_EVENT_THREAD_SPECIFIC_F : 0) - | (suppress_use ? LOG_EVENT_SUPPRESS_USE_F : 0)), + (thd_arg->thread_specific_used ? LOG_EVENT_THREAD_SPECIFIC_F : + 0) | + (suppress_use ? LOG_EVENT_SUPPRESS_USE_F : 0), using_trans), data_buf(0), query(query_arg), catalog(thd_arg->catalog), db(thd_arg->db), q_len((uint32) query_length), @@ -1501,10 +1512,10 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, error_code= (killed_status_arg == THD::NOT_KILLED) ? thd_arg->net.last_errno : ((thd_arg->system_thread & SYSTEM_THREAD_DELAYED_INSERT) ? 0 : - thd->killed_errno()); + thd_arg->killed_errno()); time(&end_time); - exec_time = (ulong) (end_time - thd->start_time); + exec_time = (ulong) (end_time - thd_arg->start_time); catalog_len = (catalog) ? (uint32) strlen(catalog) : 0; /* status_vars_len is set just before writing the event */ db_len = (db) ? (uint32) strlen(db) : 0; @@ -1513,15 +1524,15 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, /* 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 + thd_arg->options, it would be more efficient to flags2=thd_arg->options (OPTIONS_WRITTEN_TO_BINLOG would be used only at reading time). But it's likely that we don't want to use 32 bits for 3 bits; in the future we will probably want to reclaim the 29 bits. So we need the &. */ flags2= (uint32) (thd_arg->options & OPTIONS_WRITTEN_TO_BIN_LOG); - DBUG_ASSERT(thd->variables.character_set_client->number < 256*256); - DBUG_ASSERT(thd->variables.collation_connection->number < 256*256); - DBUG_ASSERT(thd->variables.collation_server->number < 256*256); + DBUG_ASSERT(thd_arg->variables.character_set_client->number < 256*256); + DBUG_ASSERT(thd_arg->variables.collation_connection->number < 256*256); + DBUG_ASSERT(thd_arg->variables.collation_server->number < 256*256); int2store(charset, thd_arg->variables.character_set_client->number); int2store(charset+2, thd_arg->variables.collation_connection->number); int2store(charset+4, thd_arg->variables.collation_server->number); @@ -1920,13 +1931,13 @@ void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) -int Query_log_event::do_apply_event(RELAY_LOG_INFO const *rli) +int Query_log_event::do_apply_event(Relay_log_info const *rli) { return do_apply_event(rli, query, q_len); } -int Query_log_event::do_apply_event(RELAY_LOG_INFO const *rli, +int Query_log_event::do_apply_event(Relay_log_info const *rli, const char *query_arg, uint32 q_len_arg) { LEX_STRING new_db; @@ -1955,11 +1966,11 @@ int Query_log_event::do_apply_event(RELAY_LOG_INFO const *rli, END of the current log event (COMMIT). We save it in rli so that InnoDB can access it. */ - const_cast<RELAY_LOG_INFO*>(rli)->future_group_master_log_pos= log_pos; + const_cast<Relay_log_info*>(rli)->future_group_master_log_pos= log_pos; DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos)); - clear_all_errors(thd, const_cast<RELAY_LOG_INFO*>(rli)); - const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock(); + clear_all_errors(thd, const_cast<Relay_log_info*>(rli)); + const_cast<Relay_log_info*>(rli)->clear_tables_to_lock(); /* Note: We do not need to execute reset_one_shot_variables() if this @@ -2073,7 +2084,7 @@ int Query_log_event::do_apply_event(RELAY_LOG_INFO const *rli, /* Execute the query (note that we bypass dispatch_command()) */ const char* found_semicolon= NULL; mysql_parse(thd, thd->query, thd->query_length, &found_semicolon); - + log_slow_statement(thd); } else { @@ -2085,7 +2096,7 @@ int Query_log_event::do_apply_event(RELAY_LOG_INFO const *rli, to check/fix it. */ if (mysql_test_parse_for_slave(thd, thd->query, thd->query_length)) - clear_all_errors(thd, const_cast<RELAY_LOG_INFO*>(rli)); /* Can ignore query */ + clear_all_errors(thd, const_cast<Relay_log_info*>(rli)); /* Can ignore query */ else { rli->report(ERROR_LEVEL, expected_error, @@ -2136,7 +2147,7 @@ Default database: '%s'. Query: '%s'", ignored_error_code(actual_error)) { DBUG_PRINT("info",("error ignored")); - clear_all_errors(thd, const_cast<RELAY_LOG_INFO*>(rli)); + clear_all_errors(thd, const_cast<Relay_log_info*>(rli)); thd->killed= THD::NOT_KILLED; } /* @@ -2207,7 +2218,7 @@ end: return thd->query_error; } -int Query_log_event::do_update_pos(RELAY_LOG_INFO *rli) +int Query_log_event::do_update_pos(Relay_log_info *rli) { /* Note that we will not increment group* positions if we are just @@ -2247,9 +2258,10 @@ Muted_query_log_event::Muted_query_log_event() **************************************************************************/ #ifndef MYSQL_CLIENT -Start_log_event_v3::Start_log_event_v3() :Log_event(), binlog_version(BINLOG_VERSION), artificial_event(0) +Start_log_event_v3::Start_log_event_v3() + :Log_event(), created(0), binlog_version(BINLOG_VERSION), + artificial_event(0), dont_set_created(0) { - created= when; memcpy(server_version, ::server_version, ST_SERVER_VER_LEN); } #endif @@ -2319,7 +2331,8 @@ void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info) */ Start_log_event_v3::Start_log_event_v3(const char* buf, - const Format_description_log_event* description_event) + const Format_description_log_event + *description_event) :Log_event(buf, description_event) { buf+= description_event->common_header_len; @@ -2331,6 +2344,7 @@ Start_log_event_v3::Start_log_event_v3(const char* buf, 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); + dont_set_created= 1; } @@ -2344,6 +2358,8 @@ bool Start_log_event_v3::write(IO_CACHE* file) char buff[START_V3_HEADER_LEN]; int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version); memcpy(buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN); + if (!dont_set_created) + created= when= get_time(); int4store(buff + ST_CREATED_OFFSET,created); return (write_header(file, sizeof(buff)) || my_b_safe_write(file, (uchar*) buff, sizeof(buff))); @@ -2371,11 +2387,10 @@ bool Start_log_event_v3::write(IO_CACHE* file) */ #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) -int Start_log_event_v3::do_apply_event(RELAY_LOG_INFO const *rli) +int Start_log_event_v3::do_apply_event(Relay_log_info const *rli) { DBUG_ENTER("Start_log_event_v3::do_apply_event"); - switch (binlog_version) - { + switch (binlog_version) { case 3: case 4: /* @@ -2446,7 +2461,6 @@ Format_description_log_event:: Format_description_log_event(uint8 binlog_ver, const char* server_ver) :Start_log_event_v3() { - created= when; binlog_version= binlog_ver; switch (binlog_ver) { case 4: /* MySQL 5.0 */ @@ -2598,6 +2612,8 @@ bool Format_description_log_event::write(IO_CACHE* file) uchar buff[FORMAT_DESCRIPTION_HEADER_LEN]; int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version); memcpy((char*) buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN); + if (!dont_set_created) + created= when= get_time(); int4store(buff + ST_CREATED_OFFSET,created); buff[ST_COMMON_HEADER_LEN_OFFSET]= LOG_EVENT_HEADER_LEN; memcpy((char*) buff+ST_COMMON_HEADER_LEN_OFFSET+1, (uchar*) post_header_len, @@ -2608,7 +2624,7 @@ bool Format_description_log_event::write(IO_CACHE* file) #endif #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) -int Format_description_log_event::do_apply_event(RELAY_LOG_INFO const *rli) +int Format_description_log_event::do_apply_event(Relay_log_info const *rli) { DBUG_ENTER("Format_description_log_event::do_apply_event"); @@ -2632,7 +2648,7 @@ int Format_description_log_event::do_apply_event(RELAY_LOG_INFO const *rli) "or ROLLBACK in relay log). A probable cause is that " "the master died while writing the transaction to " "its binary log, thus rolled back too."); - const_cast<RELAY_LOG_INFO*>(rli)->cleanup_context(thd, 1); + const_cast<Relay_log_info*>(rli)->cleanup_context(thd, 1); } #endif /* @@ -2656,7 +2672,7 @@ int Format_description_log_event::do_apply_event(RELAY_LOG_INFO const *rli) DBUG_RETURN(0); } -int Format_description_log_event::do_update_pos(RELAY_LOG_INFO *rli) +int Format_description_log_event::do_update_pos(Relay_log_info *rli) { /* save the information describing this binlog */ delete rli->relay_log.description_event_for_exec; @@ -2687,7 +2703,7 @@ int Format_description_log_event::do_update_pos(RELAY_LOG_INFO *rli) } Log_event::enum_skip_reason -Format_description_log_event::do_shall_skip(RELAY_LOG_INFO *rli) +Format_description_log_event::do_shall_skip(Relay_log_info *rli) { return Log_event::EVENT_SKIP_NOT; } @@ -2912,8 +2928,9 @@ Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex, List<Item> &fields_arg, enum enum_duplicates handle_dup, bool ignore, bool using_trans) - :Log_event(thd_arg, !thd_arg->tmp_table_used ? - 0 : LOG_EVENT_THREAD_SPECIFIC_F, using_trans), + :Log_event(thd_arg, + thd_arg->thread_specific_used ? LOG_EVENT_THREAD_SPECIFIC_F : 0, + using_trans), thread_id(thd_arg->thread_id), slave_proxy_id(thd_arg->variables.pseudo_thread_id), num_fields(0),fields(0), @@ -3229,7 +3246,7 @@ void Load_log_event::set_fields(const char* affected_db, 1 Failure */ -int Load_log_event::do_apply_event(NET* net, RELAY_LOG_INFO const *rli, +int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli, bool use_rli_only_for_errors) { LEX_STRING new_db; @@ -3239,7 +3256,7 @@ int Load_log_event::do_apply_event(NET* net, RELAY_LOG_INFO const *rli, DBUG_ASSERT(thd->query == 0); thd->query_length= 0; // Should not be needed thd->query_error= 0; - clear_all_errors(thd, const_cast<RELAY_LOG_INFO*>(rli)); + clear_all_errors(thd, const_cast<Relay_log_info*>(rli)); /* see Query_log_event::do_apply_event() and BUG#13360 */ DBUG_ASSERT(!rli->m_table_map.count()); @@ -3256,7 +3273,7 @@ int Load_log_event::do_apply_event(NET* net, RELAY_LOG_INFO const *rli, Saved for InnoDB, see comment in Query_log_event::do_apply_event() */ - const_cast<RELAY_LOG_INFO*>(rli)->future_group_master_log_pos= log_pos; + const_cast<Relay_log_info*>(rli)->future_group_master_log_pos= log_pos; DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos)); } @@ -3620,7 +3637,7 @@ bool Rotate_log_event::write(IO_CACHE* file) */ #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) -int Rotate_log_event::do_update_pos(RELAY_LOG_INFO *rli) +int Rotate_log_event::do_update_pos(Relay_log_info *rli) { DBUG_ENTER("Rotate_log_event::do_update_pos"); #ifndef DBUG_OFF @@ -3690,7 +3707,7 @@ int Rotate_log_event::do_update_pos(RELAY_LOG_INFO *rli) Log_event::enum_skip_reason -Rotate_log_event::do_shall_skip(RELAY_LOG_INFO *rli) +Rotate_log_event::do_shall_skip(Relay_log_info *rli) { enum_skip_reason reason= Log_event::do_shall_skip(rli); @@ -3816,13 +3833,13 @@ void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) */ #if defined(HAVE_REPLICATION)&& !defined(MYSQL_CLIENT) -int Intvar_log_event::do_apply_event(RELAY_LOG_INFO const *rli) +int Intvar_log_event::do_apply_event(Relay_log_info const *rli) { /* We are now in a statement until the associated query log event has been processed. */ - const_cast<RELAY_LOG_INFO*>(rli)->set_flag(RELAY_LOG_INFO::IN_STMT); + const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT); switch (type) { case LAST_INSERT_ID_EVENT: @@ -3836,7 +3853,7 @@ int Intvar_log_event::do_apply_event(RELAY_LOG_INFO const *rli) return 0; } -int Intvar_log_event::do_update_pos(RELAY_LOG_INFO *rli) +int Intvar_log_event::do_update_pos(Relay_log_info *rli) { rli->inc_event_relay_log_pos(); return 0; @@ -3844,7 +3861,7 @@ int Intvar_log_event::do_update_pos(RELAY_LOG_INFO *rli) Log_event::enum_skip_reason -Intvar_log_event::do_shall_skip(RELAY_LOG_INFO *rli) +Intvar_log_event::do_shall_skip(Relay_log_info *rli) { /* It is a common error to set the slave skip counter to 1 instead of @@ -3922,20 +3939,20 @@ void Rand_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) -int Rand_log_event::do_apply_event(RELAY_LOG_INFO const *rli) +int Rand_log_event::do_apply_event(Relay_log_info const *rli) { /* We are now in a statement until the associated query log event has been processed. */ - const_cast<RELAY_LOG_INFO*>(rli)->set_flag(RELAY_LOG_INFO::IN_STMT); + const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT); thd->rand.seed1= (ulong) seed1; thd->rand.seed2= (ulong) seed2; return 0; } -int Rand_log_event::do_update_pos(RELAY_LOG_INFO *rli) +int Rand_log_event::do_update_pos(Relay_log_info *rli) { rli->inc_event_relay_log_pos(); return 0; @@ -3943,7 +3960,7 @@ int Rand_log_event::do_update_pos(RELAY_LOG_INFO *rli) Log_event::enum_skip_reason -Rand_log_event::do_shall_skip(RELAY_LOG_INFO *rli) +Rand_log_event::do_shall_skip(Relay_log_info *rli) { /* It is a common error to set the slave skip counter to 1 instead of @@ -4025,7 +4042,7 @@ void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) -int Xid_log_event::do_apply_event(RELAY_LOG_INFO const *rli) +int Xid_log_event::do_apply_event(Relay_log_info const *rli) { /* For a slave Xid_log_event is COMMIT */ general_log_print(thd, COM_QUERY, @@ -4322,7 +4339,7 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) */ #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) -int User_var_log_event::do_apply_event(RELAY_LOG_INFO const *rli) +int User_var_log_event::do_apply_event(Relay_log_info const *rli) { Item *it= 0; CHARSET_INFO *charset; @@ -4338,7 +4355,7 @@ int User_var_log_event::do_apply_event(RELAY_LOG_INFO const *rli) We are now in a statement until the associated query log event has been processed. */ - const_cast<RELAY_LOG_INFO*>(rli)->set_flag(RELAY_LOG_INFO::IN_STMT); + const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT); if (is_null) { @@ -4349,7 +4366,7 @@ int User_var_log_event::do_apply_event(RELAY_LOG_INFO const *rli) switch (type) { case REAL_RESULT: float8get(real_val, val); - it= new Item_float(real_val); + it= new Item_float(real_val, 0); val= (char*) &real_val; // Pointer to value in native format val_len= 8; break; @@ -4393,14 +4410,14 @@ int User_var_log_event::do_apply_event(RELAY_LOG_INFO const *rli) return 0; } -int User_var_log_event::do_update_pos(RELAY_LOG_INFO *rli) +int User_var_log_event::do_update_pos(Relay_log_info *rli) { rli->inc_event_relay_log_pos(); return 0; } Log_event::enum_skip_reason -User_var_log_event::do_shall_skip(RELAY_LOG_INFO *rli) +User_var_log_event::do_shall_skip(Relay_log_info *rli) { /* It is a common error to set the slave skip counter to 1 instead @@ -4454,14 +4471,14 @@ void Slave_log_event::pack_info(Protocol *protocol) #ifndef MYSQL_CLIENT Slave_log_event::Slave_log_event(THD* thd_arg, - RELAY_LOG_INFO* rli) + Relay_log_info* rli) :Log_event(thd_arg, 0, 0) , mem_pool(0), master_host(0) { DBUG_ENTER("Slave_log_event"); if (!rli->inited) // QQ When can this happen ? DBUG_VOID_RETURN; - MASTER_INFO* mi = rli->mi; + Master_info* mi = rli->mi; // TODO: re-write this better without holding both locks at the same time pthread_mutex_lock(&mi->data_lock); pthread_mutex_lock(&rli->data_lock); @@ -4564,7 +4581,7 @@ Slave_log_event::Slave_log_event(const char* buf, uint event_len) #ifndef MYSQL_CLIENT -int Slave_log_event::do_apply_event(RELAY_LOG_INFO const *rli) +int Slave_log_event::do_apply_event(Relay_log_info const *rli) { if (mysql_bin_log.is_open()) mysql_bin_log.write(this); @@ -4611,7 +4628,7 @@ void Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) */ #ifndef MYSQL_CLIENT -int Stop_log_event::do_update_pos(RELAY_LOG_INFO *rli) +int Stop_log_event::do_update_pos(Relay_log_info *rli) { /* We do not want to update master_log pos because we get a rotate event @@ -4824,7 +4841,7 @@ void Create_file_log_event::pack_info(Protocol *protocol) */ #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) -int Create_file_log_event::do_apply_event(RELAY_LOG_INFO const *rli) +int Create_file_log_event::do_apply_event(Relay_log_info const *rli) { char proc_info[17+FN_REFLEN+10], *fname_buf; char *ext; @@ -4903,8 +4920,9 @@ err: */ #ifndef MYSQL_CLIENT -Append_block_log_event::Append_block_log_event(THD* thd_arg, const char* db_arg, - char* block_arg, +Append_block_log_event::Append_block_log_event(THD *thd_arg, + const char *db_arg, + char *block_arg, uint block_len_arg, bool using_trans) :Log_event(thd_arg,0, using_trans), block(block_arg), @@ -5000,7 +5018,7 @@ int Append_block_log_event::get_create_or_append() const Append_block_log_event::do_apply_event() */ -int Append_block_log_event::do_apply_event(RELAY_LOG_INFO const *rli) +int Append_block_log_event::do_apply_event(Relay_log_info const *rli) { char proc_info[17+FN_REFLEN+10], *fname= proc_info+17; int fd; @@ -5132,7 +5150,7 @@ void Delete_file_log_event::pack_info(Protocol *protocol) */ #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) -int Delete_file_log_event::do_apply_event(RELAY_LOG_INFO const *rli) +int Delete_file_log_event::do_apply_event(Relay_log_info const *rli) { char fname[FN_REFLEN+10]; char *ext= slave_load_file_stem(fname, file_id, server_id, ".data"); @@ -5153,7 +5171,8 @@ int Delete_file_log_event::do_apply_event(RELAY_LOG_INFO const *rli) */ #ifndef MYSQL_CLIENT -Execute_load_log_event::Execute_load_log_event(THD *thd_arg, const char* db_arg, +Execute_load_log_event::Execute_load_log_event(THD *thd_arg, + const char* db_arg, bool using_trans) :Log_event(thd_arg, 0, using_trans), file_id(thd_arg->file_id), db(db_arg) { @@ -5228,7 +5247,7 @@ void Execute_load_log_event::pack_info(Protocol *protocol) Execute_load_log_event::do_apply_event() */ -int Execute_load_log_event::do_apply_event(RELAY_LOG_INFO const *rli) +int Execute_load_log_event::do_apply_event(Relay_log_info const *rli) { char fname[FN_REFLEN+10]; char *ext; @@ -5267,7 +5286,7 @@ int Execute_load_log_event::do_apply_event(RELAY_LOG_INFO const *rli) calls mysql_load()). */ - const_cast<RELAY_LOG_INFO*>(rli)->future_group_master_log_pos= log_pos; + const_cast<Relay_log_info*>(rli)->future_group_master_log_pos= log_pos; if (lev->do_apply_event(0,rli,1)) { /* @@ -5354,7 +5373,7 @@ int Begin_load_query_log_event::get_create_or_append() const #ifndef MYSQL_CLIENT Execute_load_query_log_event:: -Execute_load_query_log_event(THD* thd_arg, const char* query_arg, +Execute_load_query_log_event(THD *thd_arg, const char* query_arg, ulong query_length_arg, uint fn_pos_start_arg, uint fn_pos_end_arg, enum_load_dup_handling dup_handling_arg, @@ -5477,7 +5496,7 @@ void Execute_load_query_log_event::pack_info(Protocol *protocol) int -Execute_load_query_log_event::do_apply_event(RELAY_LOG_INFO const *rli) +Execute_load_query_log_event::do_apply_event(Relay_log_info const *rli) { char *p; char *buf; @@ -5634,14 +5653,16 @@ Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid, m_table(tbl_arg), m_table_id(tid), m_width(tbl_arg ? tbl_arg->s->fields : 1), - m_rows_buf(0), m_rows_cur(0), m_rows_end(0), - m_flags(0) + m_rows_buf(0), m_rows_cur(0), m_rows_end(0), m_flags(0) +#ifdef HAVE_REPLICATION + ,m_key(NULL), m_curr_row(NULL), m_curr_row_end(NULL) +#endif { /* We allow a special form of dummy event when the table, and cols are null and the table id is ~0UL. This is a temporary solution, to be able to terminate a started statement in the - binary log: the extreneous events will be removed in the future. + binary log: the extraneous events will be removed in the future. */ DBUG_ASSERT(tbl_arg && tbl_arg->s && tid != ~0UL || !tbl_arg && !cols && tid == ~0UL); @@ -5650,15 +5671,18 @@ Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid, set_flags(NO_FOREIGN_KEY_CHECKS_F); if (thd_arg->options & OPTION_RELAXED_UNIQUE_CHECKS) set_flags(RELAXED_UNIQUE_CHECKS_F); - /* if bitmap_init fails, catched in is_valid() */ + /* if bitmap_init fails, caught in is_valid() */ if (likely(!bitmap_init(&m_cols, m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL, - (m_width + 7) & ~7UL, + m_width, false))) { /* Cols can be zero if this is a dummy binrows event */ if (likely(cols != NULL)) + { memcpy(m_cols.bitmap, cols->bitmap, no_bytes_in_map(cols)); + create_last_word_mask(&m_cols); + } } else { @@ -5674,7 +5698,13 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len, *description_event) : Log_event(buf, description_event), m_row_count(0), - m_rows_buf(0), m_rows_cur(0), m_rows_end(0) +#ifndef MYSQL_CLIENT + m_table(NULL), +#endif + m_table_id(0), m_rows_buf(0), m_rows_cur(0), m_rows_end(0) +#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) + ,m_key(NULL), m_curr_row(NULL), m_curr_row_end(NULL) +#endif { DBUG_ENTER("Rows_log_event::Rows_log_event(const char*,...)"); uint8 const common_header_len= description_event->common_header_len; @@ -5711,11 +5741,12 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len, /* if bitmap_init fails, catched in is_valid() */ if (likely(!bitmap_init(&m_cols, m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL, - (m_width + 7) & ~7UL, + m_width, false))) { DBUG_PRINT("debug", ("Reading from %p", ptr_after_width)); memcpy(m_cols.bitmap, ptr_after_width, (m_width + 7) / 8); + create_last_word_mask(&m_cols); ptr_after_width+= (m_width + 7) / 8; DBUG_DUMP("m_cols", (uchar*) m_cols.bitmap, no_bytes_in_map(&m_cols)); } @@ -5732,14 +5763,15 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len, { DBUG_PRINT("debug", ("Reading from %p", ptr_after_width)); - /* if bitmap_init fails, catched in is_valid() */ + /* if bitmap_init fails, caught in is_valid() */ if (likely(!bitmap_init(&m_cols_ai, m_width <= sizeof(m_bitbuf_ai)*8 ? m_bitbuf_ai : NULL, - (m_width + 7) & ~7UL, + m_width, false))) { DBUG_PRINT("debug", ("Reading from %p", ptr_after_width)); memcpy(m_cols_ai.bitmap, ptr_after_width, (m_width + 7) / 8); + create_last_word_mask(&m_cols_ai); ptr_after_width+= (m_width + 7) / 8; DBUG_DUMP("m_cols_ai", (uchar*) m_cols_ai.bitmap, no_bytes_in_map(&m_cols_ai)); @@ -5761,6 +5793,9 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len, m_rows_buf= (uchar*) my_malloc(data_size, MYF(MY_WME)); if (likely((bool)m_rows_buf)) { +#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) + m_curr_row= m_rows_buf; +#endif m_rows_end= m_rows_buf + data_size; m_rows_cur= m_rows_end; memcpy(m_rows_buf, ptr_rows_data, data_size); @@ -5861,11 +5896,10 @@ int Rows_log_event::do_add_row_data(uchar *row_data, size_t length) #endif #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) -int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli) +int Rows_log_event::do_apply_event(Relay_log_info const *rli) { - DBUG_ENTER("Rows_log_event::do_apply_event(st_relay_log_info*)"); + DBUG_ENTER("Rows_log_event::do_apply_event(Relay_log_info*)"); int error= 0; - uchar const *row_start= m_rows_buf; /* If m_table_id == ~0UL, then we have a dummy event that does not @@ -5881,7 +5915,7 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli) */ DBUG_ASSERT(get_flags(STMT_END_F)); - const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock(); + const_cast<Relay_log_info*>(rli)->clear_tables_to_lock(); close_thread_tables(thd); thd->clear_error(); DBUG_RETURN(0); @@ -5937,7 +5971,7 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli) "Error in %s event: when locking tables", get_type_str()); } - const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock(); + const_cast<Relay_log_info*>(rli)->clear_tables_to_lock(); DBUG_RETURN(error); } @@ -5974,7 +6008,7 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli) "unexpected success or fatal error")); thd->query_error= 1; } - const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock(); + const_cast<Relay_log_info*>(rli)->clear_tables_to_lock(); DBUG_RETURN(error); } } @@ -5996,7 +6030,7 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli) mysql_unlock_tables(thd, thd->lock); thd->lock= 0; thd->query_error= 1; - const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock(); + const_cast<Relay_log_info*>(rli)->clear_tables_to_lock(); DBUG_RETURN(ERR_BAD_TABLE_DEF); } } @@ -6018,17 +6052,16 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli) */ 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); + const_cast<Relay_log_info*>(rli)->m_table_map.set_table(ptr->table_id, ptr->table); } #ifdef HAVE_QUERY_CACHE query_cache.invalidate_locked_for_write(rli->tables_to_lock); #endif - const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock(); } - DBUG_ASSERT(rli->tables_to_lock == NULL && rli->tables_to_lock_count == 0); - - TABLE* table= const_cast<RELAY_LOG_INFO*>(rli)->m_table_map.get_table(m_table_id); + TABLE* + table= + m_table= const_cast<Relay_log_info*>(rli)->m_table_map.get_table(m_table_id); if (table) { @@ -6073,24 +6106,43 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli) inside a statement and halting abruptly might cause problems when restarting. */ - const_cast<RELAY_LOG_INFO*>(rli)->set_flag(RELAY_LOG_INFO::IN_STMT); + const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT); - error= do_before_row_operations(table); - while (error == 0 && row_start < m_rows_end) - { - uchar const *row_end= NULL; - 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 + if ( m_width == table->s->fields && bitmap_is_set_all(&m_cols)) + set_flags(COMPLETE_ROWS_F); + + /* + Set tables write and read sets. + + Read_set contains all slave columns (in case we are going to fetch + a complete record from slave) + + Write_set equals the m_cols bitmap sent from master but it can be + longer if slave has extra columns. + */ + + DBUG_PRINT_BITSET("debug", "Setting table's write_set from: %s", &m_cols); + + bitmap_set_all(table->read_set); + bitmap_set_all(table->write_set); + if (!get_flags(COMPLETE_ROWS_F)) + bitmap_intersect(table->write_set,&m_cols); + + // Do event specific preparations + + error= do_before_row_operations(rli); - DBUG_ASSERT(row_end != NULL); // cannot happen - DBUG_ASSERT(row_end <= m_rows_end); + // row processing loop + while (error == 0 && m_curr_row < m_rows_end) + { /* in_use can have been set to NULL in close_tables_for_reopen */ THD* old_thd= table->in_use; if (!table->in_use) table->in_use= thd; - error= do_exec_row(table); + + error= do_exec_row(rli); + table->in_use = old_thd; switch (error) { @@ -6104,31 +6156,57 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli) default: rli->report(ERROR_LEVEL, thd->net.last_errno, - "Error in %s event: row application failed", - get_type_str()); + "Error in %s event: row application failed. %s", + get_type_str(), + thd->net.last_error ? thd->net.last_error : ""); thd->query_error= 1; break; } - row_start= row_end; - } + /* + If m_curr_row_end was not set during event execution (e.g., because + of errors) we can't proceed to the next row. If the error is transient + (i.e., error==0 at this point) we must call unpack_current_row() to set + m_curr_row_end. + */ + + if (!m_curr_row_end && !error) + unpack_current_row(rli); + + // at this moment m_curr_row_end should be set + DBUG_ASSERT(error || m_curr_row_end != NULL); + DBUG_ASSERT(error || m_curr_row < m_curr_row_end); + DBUG_ASSERT(error || m_curr_row_end <= m_rows_end); + + m_curr_row= m_curr_row_end; + + } // row processing loop + DBUG_EXECUTE_IF("STOP_SLAVE_after_first_Rows_event", - const_cast<RELAY_LOG_INFO*>(rli)->abort_slave= 1;); - error= do_after_row_operations(table, error); + const_cast<Relay_log_info*>(rli)->abort_slave= 1;); + error= do_after_row_operations(rli, error); if (!cache_stmt) { DBUG_PRINT("info", ("Marked that we need to keep log")); thd->options|= OPTION_KEEP_LOG; } - } + } // if (table) + + /* + We need to delay this clear until here bacause unpack_current_row() uses + master-side table definitions stored in rli. + */ + if (rli->tables_to_lock && get_flags(STMT_END_F)) + const_cast<Relay_log_info*>(rli)->clear_tables_to_lock(); if (error) { /* error has occured during the transaction */ rli->report(ERROR_LEVEL, thd->net.last_errno, "Error in %s event: error during transaction execution " - "on table %s.%s", + "on table %s.%s. %s", get_type_str(), table->s->db.str, - table->s->table_name.str); + table->s->table_name.str, + thd->net.last_error ? thd->net.last_error : ""); /* If one day we honour --skip-slave-errors in row-based replication, and @@ -6142,7 +6220,7 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli) rollback at the caller along with sbr. */ thd->reset_current_stmt_binlog_row_based(); - const_cast<RELAY_LOG_INFO*>(rli)->cleanup_context(thd, error); + const_cast<Relay_log_info*>(rli)->cleanup_context(thd, error); thd->query_error= 1; DBUG_RETURN(error); } @@ -6169,16 +6247,16 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli) wait (reached end of last relay log and nothing gets appended there), we timeout after one minute, and notify DBA about the problem. When WL#2975 is implemented, just remove the member - st_relay_log_info::last_event_start_time and all its occurences. + Relay_log_info::last_event_start_time and all its occurrences. */ - const_cast<RELAY_LOG_INFO*>(rli)->last_event_start_time= time(0); + const_cast<Relay_log_info*>(rli)->last_event_start_time= my_time(0); } DBUG_RETURN(0); } Log_event::enum_skip_reason -Rows_log_event::do_shall_skip(RELAY_LOG_INFO *rli) +Rows_log_event::do_shall_skip(Relay_log_info *rli) { /* If the slave skip counter is 1 and this event does not end a @@ -6192,7 +6270,7 @@ Rows_log_event::do_shall_skip(RELAY_LOG_INFO *rli) } int -Rows_log_event::do_update_pos(RELAY_LOG_INFO *rli) +Rows_log_event::do_update_pos(Relay_log_info *rli) { DBUG_ENTER("Rows_log_event::do_update_pos"); int error= 0; @@ -6371,6 +6449,62 @@ void Rows_log_event::print_helper(FILE *file, Table_map_log_event member functions and support functions **************************************************************************/ +/** + @page How replication of field metadata works. + + When a table map is created, the master first calls + Table_map_log_event::save_field_metadata() which calculates how many + values will be in the field metadata. Only those fields that require the + extra data are added. The method also loops through all of the fields in + the table calling the method Field::save_field_metadata() which returns the + values for the field that will be saved in the metadata and replicated to + the slave. Once all fields have been processed, the table map is written to + the binlog adding the size of the field metadata and the field metadata to + the end of the body of the table map. + + When a table map is read on the slave, the field metadata is read from the + table map and passed to the table_def class constructor which saves the + field metadata from the table map into an array based on the type of the + field. Field metadata values not present (those fields that do not use extra + data) in the table map are initialized as zero (0). The array size is the + same as the columns for the table on the slave. + +*/ + +/** + Save the field metadata based on the real_type of the field. + The metadata saved depends on the type of the field. Some fields + store a single byte for pack_length() while others store two bytes + for field_length (max length). + + @retval 0 Ok. + + TODO: We may want to consider changing the encoding of the information. + Currently, the code attempts to minimize the number of bytes written to + the tablemap. There are at least two other alternatives; 1) using + net_store_length() to store the data allowing it to choose the number of + bytes that are appropriate thereby making the code much easier to + maintain (only 1 place to change the encoding), or 2) use a fixed number + of bytes for each field. The problem with option 1 is that net_store_length() + will use one byte if the value < 251, but 3 bytes if it is > 250. Thus, + for fields like CHAR which can be no larger than 255 characters, the method + will use 3 bytes when the value is > 250. Further, every value that is + encoded using 2 parts (e.g., pack_length, field_length) will be numerically + > 250 therefore will use 3 bytes for eah value. The problem with option 2 + is less wasteful for space but does waste 1 byte for every field that does + not encode 2 parts. +*/ +#if !defined(MYSQL_CLIENT) +int Table_map_log_event::save_field_metadata() +{ + DBUG_ENTER("Table_map_log_event::save_field_metadata"); + int index= 0; + for (unsigned int i= 0 ; i < m_table->s->fields ; i++) + index+= m_table->s->field[i]->save_field_metadata(&m_field_metadata[index]); + DBUG_RETURN(index); +} +#endif /* !defined(MYSQL_CLIENT) */ + /* Constructor used to build an event for writing to the binary log. Mats says tbl->s lives longer than this event so it's ok to copy pointers @@ -6385,9 +6519,9 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid, m_dblen(m_dbnam ? tbl->s->db.length : 0), m_tblnam(tbl->s->table_name.str), m_tbllen(tbl->s->table_name.length), - m_colcnt(tbl->s->fields), m_coltype(0), - m_table_id(tid), - m_flags(flags) + m_colcnt(tbl->s->fields), m_field_metadata(0), + m_field_metadata_size(0), m_memory(NULL), m_meta_memory(NULL), m_data_size(0), + m_table_id(tid), m_null_bits(0), m_flags(flags) { DBUG_ASSERT(m_table_id != ~0UL); /* @@ -6407,13 +6541,49 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid, m_data_size+= m_tbllen + 2; // Include length and terminating \0 m_data_size+= 1 + m_colcnt; // COLCNT and column types - /* If malloc fails, catched in is_valid() */ + /* If malloc fails, caught in is_valid() */ if ((m_memory= (uchar*) my_malloc(m_colcnt, MYF(MY_WME)))) { m_coltype= reinterpret_cast<uchar*>(m_memory); for (unsigned int i= 0 ; i < m_table->s->fields ; ++i) m_coltype[i]= m_table->field[i]->type(); } + + /* + Calculate a bitmap for the results of maybe_null() for all columns. + The bitmap is used to determine when there is a column from the master + that is not on the slave and is null and thus not in the row data during + replication. + */ + uint num_null_bytes= (m_table->s->fields + 7) / 8; + m_data_size+= num_null_bytes; + m_meta_memory= (uchar *)my_multi_malloc(MYF(MY_WME), + &m_null_bits, num_null_bytes, + &m_field_metadata, (m_colcnt * 2), + NULL); + + bzero(m_field_metadata, (m_colcnt * 2)); + + /* + Create an array for the field metadata and store it. + */ + m_field_metadata_size= save_field_metadata(); + DBUG_ASSERT(m_field_metadata_size <= (m_colcnt * 2)); + + /* + Now set the size of the data to the size of the field metadata array + plus one or two bytes for number of elements in the field metadata array. + */ + if (m_field_metadata_size > 255) + m_data_size+= m_field_metadata_size + 2; + else + m_data_size+= m_field_metadata_size + 1; + + bzero(m_null_bits, num_null_bytes); + for (unsigned int i= 0 ; i < m_table->s->fields ; ++i) + if (m_table->field[i]->maybe_null()) + m_null_bits[(i / 8)]+= 1 << (i % 8); + } #endif /* !defined(MYSQL_CLIENT) */ @@ -6427,10 +6597,15 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len, : Log_event(buf, description_event), #ifndef MYSQL_CLIENT - m_table(NULL), + m_table(NULL), #endif - m_memory(NULL) + m_dbnam(NULL), m_dblen(0), m_tblnam(NULL), m_tbllen(0), + m_colcnt(0), m_coltype(0), + m_memory(NULL), m_table_id(ULONG_MAX), m_flags(0), + m_data_size(0), m_field_metadata(0), m_field_metadata_size(0), + m_null_bits(0), m_meta_memory(NULL) { + unsigned int bytes_read= 0; DBUG_ENTER("Table_map_log_event::Table_map_log_event(const char*,uint,...)"); uint8 common_header_len= description_event->common_header_len; @@ -6488,7 +6663,7 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len, (ulong) m_tbllen, (long) (ptr_tbllen-(const uchar*)vpart), m_colcnt, (long) (ptr_colcnt-(const uchar*)vpart))); - /* Allocate mem for all fields in one go. If fails, catched in is_valid() */ + /* Allocate mem for all fields in one go. If fails, caught in is_valid() */ m_memory= (uchar*) my_multi_malloc(MYF(MY_WME), &m_dbnam, (uint) m_dblen + 1, &m_tblnam, (uint) m_tbllen + 1, @@ -6501,6 +6676,23 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len, strncpy(const_cast<char*>(m_dbnam), (const char*)ptr_dblen + 1, m_dblen + 1); strncpy(const_cast<char*>(m_tblnam), (const char*)ptr_tbllen + 1, m_tbllen + 1); memcpy(m_coltype, ptr_after_colcnt, m_colcnt); + + ptr_after_colcnt= ptr_after_colcnt + m_colcnt; + bytes_read= ptr_after_colcnt - (uchar *)buf; + DBUG_PRINT("info", ("Bytes read: %d.\n", bytes_read)); + if (bytes_read < event_len) + { + m_field_metadata_size= net_field_length(&ptr_after_colcnt); + DBUG_ASSERT(m_field_metadata_size <= (m_colcnt * 2)); + uint num_null_bytes= (m_colcnt + 7) / 8; + m_meta_memory= (uchar *)my_multi_malloc(MYF(MY_WME), + &m_null_bits, num_null_bytes, + &m_field_metadata, m_field_metadata_size, + NULL); + memcpy(m_field_metadata, ptr_after_colcnt, m_field_metadata_size); + ptr_after_colcnt= (uchar*)ptr_after_colcnt + m_field_metadata_size; + memcpy(m_null_bits, ptr_after_colcnt, num_null_bytes); + } } DBUG_VOID_RETURN; @@ -6509,6 +6701,7 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len, Table_map_log_event::~Table_map_log_event() { + my_free(m_meta_memory, MYF(MY_ALLOW_ZERO_PTR)); my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR)); } @@ -6524,13 +6717,13 @@ Table_map_log_event::~Table_map_log_event() */ #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) -int Table_map_log_event::do_apply_event(RELAY_LOG_INFO const *rli) +int Table_map_log_event::do_apply_event(Relay_log_info const *rli) { RPL_TABLE_LIST *table_list; char *db_mem, *tname_mem; size_t dummy_len; void *memory; - DBUG_ENTER("Table_map_log_event::do_apply_event(st_relay_log_info*)"); + DBUG_ENTER("Table_map_log_event::do_apply_event(Relay_log_info*)"); DBUG_ASSERT(rli->sql_thd == thd); /* Step the query id to mark what columns that are actually used. */ @@ -6636,10 +6829,11 @@ int Table_map_log_event::do_apply_event(RELAY_LOG_INFO const *rli) 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 + inside 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); + new (&table_list->m_tabledef) table_def(m_coltype, m_colcnt, + m_field_metadata, m_field_metadata_size, m_null_bits); table_list->m_tabledef_valid= TRUE; /* @@ -6647,8 +6841,8 @@ int Table_map_log_event::do_apply_event(RELAY_LOG_INFO const *rli) locked by linking the table into the list of tables to lock. */ table_list->next_global= table_list->next_local= rli->tables_to_lock; - const_cast<RELAY_LOG_INFO*>(rli)->tables_to_lock= table_list; - const_cast<RELAY_LOG_INFO*>(rli)->tables_to_lock_count++; + const_cast<Relay_log_info*>(rli)->tables_to_lock= table_list; + const_cast<Relay_log_info*>(rli)->tables_to_lock_count++; /* 'memory' is freed in clear_tables_to_lock */ } @@ -6660,7 +6854,7 @@ err: } Log_event::enum_skip_reason -Table_map_log_event::do_shall_skip(RELAY_LOG_INFO *rli) +Table_map_log_event::do_shall_skip(Relay_log_info *rli) { /* If the slave skip counter is 1, then we should not start executing @@ -6672,7 +6866,7 @@ Table_map_log_event::do_shall_skip(RELAY_LOG_INFO *rli) return Log_event::do_shall_skip(rli); } -int Table_map_log_event::do_update_pos(RELAY_LOG_INFO *rli) +int Table_map_log_event::do_update_pos(Relay_log_info *rli) { rli->inc_event_relay_log_pos(); return 0; @@ -6711,12 +6905,21 @@ bool Table_map_log_event::write_data_body(IO_CACHE *file) uchar *const cbuf_end= net_store_length(cbuf, (size_t) m_colcnt); DBUG_ASSERT(static_cast<size_t>(cbuf_end - cbuf) <= sizeof(cbuf)); + /* + Store the size of the field metadata. + */ + uchar mbuf[sizeof(m_field_metadata_size)]; + uchar *const mbuf_end= net_store_length(mbuf, m_field_metadata_size); + return (my_b_safe_write(file, dbuf, sizeof(dbuf)) || my_b_safe_write(file, (const uchar*)m_dbnam, m_dblen+1) || my_b_safe_write(file, tbuf, sizeof(tbuf)) || my_b_safe_write(file, (const uchar*)m_tblnam, m_tbllen+1) || my_b_safe_write(file, cbuf, (size_t) (cbuf_end - cbuf)) || - my_b_safe_write(file, m_coltype, m_colcnt)); + my_b_safe_write(file, m_coltype, m_colcnt) || + my_b_safe_write(file, mbuf, (size_t) (mbuf_end - mbuf)) || + my_b_safe_write(file, m_field_metadata, m_field_metadata_size), + my_b_safe_write(file, m_null_bits, (m_colcnt + 7) / 8)); } #endif @@ -6786,7 +6989,8 @@ Write_rows_log_event::Write_rows_log_event(const char *buf, uint event_len, #endif #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) -int Write_rows_log_event::do_before_row_operations(TABLE *table) +int +Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const) { int error= 0; @@ -6808,26 +7012,26 @@ int Write_rows_log_event::do_before_row_operations(TABLE *table) /* Do not raise the error flag in case of hitting to an unique attribute */ - table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); + m_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); /* NDB specific: update from ndb master wrapped as Write_rows */ /* so that the event should be applied to replace slave's row */ - table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE); + m_table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE); /* NDB specific: if update from ndb master wrapped as Write_rows does not find the row it's assumed idempotent binlog applying is taking place; don't raise the error. */ - table->file->extra(HA_EXTRA_IGNORE_NO_KEY); + m_table->file->extra(HA_EXTRA_IGNORE_NO_KEY); /* TODO: the cluster team (Tomas?) says that it's better if the engine knows how many rows are going to be inserted, then it can allocate needed memory from the start. */ - table->file->ha_start_bulk_insert(0); + m_table->file->ha_start_bulk_insert(0); /* We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill any TIMESTAMP column with data from the row but instead will use @@ -6843,45 +7047,31 @@ int Write_rows_log_event::do_before_row_operations(TABLE *table) some cases we won't want TIMESTAMP_NO_AUTO_SET (will require some code to analyze if explicit data is provided for slave's TIMESTAMP columns). */ - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; + m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; return error; } -int Write_rows_log_event::do_after_row_operations(TABLE *table, int error) +int +Write_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const, + int error) { int local_error= 0; - table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); - table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); + m_table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); + m_table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); /* reseting the extra with table->file->extra(HA_EXTRA_NO_IGNORE_NO_KEY); fires bug#27077 todo: explain or fix */ - if ((local_error= table->file->ha_end_bulk_insert())) + if ((local_error= m_table->file->ha_end_bulk_insert())) { - table->file->print_error(local_error, MYF(0)); + m_table->file->print_error(local_error, MYF(0)); } return error? error : local_error; } -int Write_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO const *rli, - TABLE *table, - uchar const *const row_start, - uchar const **const row_end) -{ - DBUG_ASSERT(table != NULL); - DBUG_ASSERT(row_start && row_end); - - if (int error= unpack_row(rli, table, m_width, row_start, &m_cols, row_end, - &m_master_reclength, table->write_set, WRITE_ROWS_EVENT)) - { - thd->net.last_errno= error; - return error; - } - bitmap_copy(table->read_set, table->write_set); - return 0; -} +#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) /* Check if there are more UNIQUE keys after the given key. @@ -6895,130 +7085,6 @@ last_uniq_key(TABLE *table, uint keyno) return 1; } -/* Anonymous namespace for template functions/classes */ -namespace { - - /* - Smart pointer that will automatically call my_afree (a macro) when - the pointer goes out of scope. This is used so that I do not have - to remember to call my_afree() before each return. There is no - overhead associated with this, since all functions are inline. - - I (Matz) would prefer to use the free function as a template - parameter, but that is not possible when the "function" is a - macro. - */ - template <class Obj> - class auto_afree_ptr - { - Obj* m_ptr; - public: - auto_afree_ptr(Obj* ptr) : m_ptr(ptr) { } - ~auto_afree_ptr() { if (m_ptr) my_afree(m_ptr); } - void assign(Obj* ptr) { - /* Only to be called if it hasn't been given a value before. */ - DBUG_ASSERT(m_ptr == NULL); - m_ptr= ptr; - } - Obj* get() { return m_ptr; } - }; - -} - - -/* - Copy "extra" columns from record[1] to record[0]. - - Copy the extra fields that are not present on the master but are - present on the slave from record[1] to record[0]. This is used - after fetching a record that are to be updated, either inside - replace_record() or as part of executing an update_row(). - */ -static int -copy_extra_record_fields(TABLE *table, - size_t master_reclength, - my_ptrdiff_t master_fields) -{ - 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 - master into record[0] (which are basically the default values). - */ - DBUG_ASSERT(master_reclength <= table->s->reclength); - if (master_reclength < table->s->reclength) - bmove_align(table->record[0] + master_reclength, - table->record[1] + master_reclength, - table->s->reclength - master_reclength); - - /* - Bit columns are special. We iterate over all the remaining - columns and copy the "extra" bits to the new record. This is - not a very good solution: it should be refactored on - opportunity. - - REFACTORING SUGGESTION (Matz). Introduce a member function - similar to move_field_offset() called copy_field_offset() to - copy field values and implement it for all Field subclasses. Use - this function to copy data from the found record to the record - that are going to be inserted. - - The copy_field_offset() function need to be a virtual function, - which in this case will prevent copying an entire range of - fields efficiently. - */ - { - Field **field_ptr= table->field + master_fields; - for ( ; *field_ptr ; ++field_ptr) - { - /* - Set the null bit according to the values in record[1] - */ - if ((*field_ptr)->maybe_null() && - (*field_ptr)->is_null_in_record(reinterpret_cast<uchar*>(table->record[1]))) - (*field_ptr)->set_null(); - else - (*field_ptr)->set_notnull(); - - /* - Do the extra work for special columns. - */ - switch ((*field_ptr)->real_type()) - { - default: - /* Nothing to do */ - break; - - case MYSQL_TYPE_BIT: - Field_bit *f= static_cast<Field_bit*>(*field_ptr); - 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; - } - } - } - return 0; // All OK -} - -#define DBUG_PRINT_BITSET(N,FRM,BS) \ - do { \ - char buf[256]; \ - for (uint i = 0 ; i < (BS)->n_bits ; ++i) \ - buf[i] = bitmap_is_set((BS), i) ? '1' : '0'; \ - buf[(BS)->n_bits] = '\0'; \ - DBUG_PRINT((N), ((FRM), buf)); \ - } while (0) - - /** Check if an error is a duplicate key error. @@ -7044,45 +7110,76 @@ is_duplicate_key_error(int errcode) return false; } +/** + Write the current row into event's table. -/* - Replace the provided record in the database. + The row is located in the row buffer, pointed by @c m_curr_row member. + Number of columns of the row is stored in @c m_width member (it can be + different from the number of columns in the table to which we insert). + Bitmap @c m_cols indicates which columns are present in the row. It is assumed + that event's table is already open and pointed by @c m_table. - SYNOPSIS - replace_record() - thd Thread context for writing the record. - table Table to which record should be written. - master_reclength - Offset to first column that is not present on the master, - alternatively the length of the record on the master - side. + If the same record already exists in the table it can be either overwritten + or an error is reported depending on the value of @c overwrite flag + (error reporting not yet implemented). Note that the matching record can be + different from the row we insert if we use primary keys to identify records in + the table. - RETURN VALUE - Error code on failure, 0 on success. + The row to be inserted can contain values only for selected columns. The + missing columns are filled with default values using @c prepare_record() + function. If a matching record is found in the table and @c overwritte is + true, the missing columns are taken from it. - DESCRIPTION - Similar to how it is done in mysql_insert(), we first try to do - a ha_write_row() and of that fails due to duplicated keys (or - indices), we do an ha_update_row() or a ha_delete_row() instead. - */ -static int -replace_record(THD *thd, TABLE *table, - ulong const master_reclength, - uint const master_fields) + @param rli Relay log info (needed for row unpacking). + @param overwrite + Shall we overwrite if the row already exists or signal + error (currently ignored). + + @returns Error code on failure, 0 on success. + + This method, if successful, sets @c m_curr_row_end pointer to point at the + next row in the rows buffer. This is done when unpacking the row to be + inserted. + + @note If a matching record is found, it is either updated using + @c ha_update_row() or first deleted and then new record written. +*/ + +int +Rows_log_event::write_row(const Relay_log_info *const rli, + const bool overwrite) { - DBUG_ENTER("replace_record"); - DBUG_ASSERT(table != NULL && thd != NULL); + DBUG_ENTER("write_row"); + DBUG_ASSERT(m_table != NULL && thd != NULL); + TABLE *table= m_table; // pointer to event's table int error; int keynum; auto_afree_ptr<char> key(NULL); + /* fill table->record[0] with default values */ + + if ((error= prepare_record(rli, table, m_width, + TRUE /* check if columns have def. values */))) + DBUG_RETURN(error); + + /* unpack row into table->record[0] */ + error= unpack_current_row(rli); // TODO: how to handle errors? + #ifndef DBUG_OFF DBUG_DUMP("record[0]", table->record[0], table->s->reclength); DBUG_PRINT_BITSET("debug", "write_set = %s", table->write_set); DBUG_PRINT_BITSET("debug", "read_set = %s", table->read_set); #endif + /* + Try to write record. If a corresponding record already exists in the table, + we try to change it using ha_update_row() if possible. Otherwise we delete + it and repeat the whole process again. + + TODO: Add safety measures against infinite looping. + */ + while ((error= table->file->ha_write_row(table->record[0]))) { if (error == HA_ERR_LOCK_DEADLOCK || error == HA_ERR_LOCK_WAIT_TIMEOUT) @@ -7092,7 +7189,13 @@ replace_record(THD *thd, TABLE *table, } if ((keynum= table->file->get_dup_key(error)) < 0) { - /* We failed to retrieve the duplicate key */ + DBUG_PRINT("info",("Can't locate duplicate key (get_dup_key returns %d)",keynum)); + table->file->print_error(error, MYF(0)); + /* + We failed to retrieve the duplicate key + - either because the error was not "duplicate key" error + - or because the information which key is not available + */ DBUG_RETURN(error); } @@ -7108,17 +7211,22 @@ replace_record(THD *thd, TABLE *table, */ if (table->file->ha_table_flags() & HA_DUPLICATE_POS) { + DBUG_PRINT("info",("Locating offending record using rnd_pos()")); error= table->file->rnd_pos(table->record[1], table->file->dup_ref); if (error) { + DBUG_PRINT("info",("rnd_pos() returns error %d",error)); table->file->print_error(error, MYF(0)); DBUG_RETURN(error); } } else { + DBUG_PRINT("info",("Locating offending record using index_read_idx()")); + if (table->file->extra(HA_EXTRA_FLUSH_CACHE)) { + DBUG_PRINT("info",("Error when setting HA_EXTRA_FLUSH_CACHE")); DBUG_RETURN(my_errno); } @@ -7126,30 +7234,47 @@ replace_record(THD *thd, TABLE *table, { key.assign(static_cast<char*>(my_alloca(table->s->max_unique_length))); if (key.get() == NULL) + { + DBUG_PRINT("info",("Can't allocate key buffer")); DBUG_RETURN(ENOMEM); + } } - key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum, 0); - error= table->file->index_read_idx(table->record[1], keynum, - (const uchar*)key.get(), - HA_WHOLE_KEY, - HA_READ_KEY_EXACT); + key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum, + 0); + error= table->file->index_read_idx_map(table->record[1], keynum, + (const uchar*)key.get(), + HA_WHOLE_KEY, + HA_READ_KEY_EXACT); if (error) { + DBUG_PRINT("info",("index_read_idx() returns error %d",error)); table->file->print_error(error, MYF(0)); DBUG_RETURN(error); } } /* - Now, table->record[1] should contain the offending row. That + Now, record[1] should contain the offending row. That will enable us to update it or, alternatively, delete it (so that we can insert the new row afterwards). + */ - First we copy the columns into table->record[0] that are not - present on the master from table->record[1], if there are any. + /* + If row is incomplete we will use the record found to fill + missing columns. */ - copy_extra_record_fields(table, master_reclength, master_fields); + if (!get_flags(COMPLETE_ROWS_F)) + { + restore_record(table,record[1]); + error= unpack_current_row(rli); + } + +#ifndef DBUG_OFF + DBUG_PRINT("debug",("preparing for update: before and after image")); + DBUG_DUMP("record[1] (before)", table->record[1], table->s->reclength); + DBUG_DUMP("record[0] (after)", table->record[0], table->s->reclength); +#endif /* REPLACE is defined as either INSERT or DELETE + INSERT. If @@ -7169,18 +7294,32 @@ replace_record(THD *thd, TABLE *table, if (last_uniq_key(table, keynum) && !table->file->referenced_by_foreign_key()) { + DBUG_PRINT("info",("Updating row using ha_update_row()")); error=table->file->ha_update_row(table->record[1], table->record[0]); - if (error && error != HA_ERR_RECORD_IS_THE_SAME) - table->file->print_error(error, MYF(0)); - else + switch (error) { + + case HA_ERR_RECORD_IS_THE_SAME: + DBUG_PRINT("info",("ignoring HA_ERR_RECORD_IS_THE_SAME error from" + " ha_update_row()")); error= 0; + + case 0: + break; + + default: + DBUG_PRINT("info",("ha_update_row() returns error %d",error)); + table->file->print_error(error, MYF(0)); + } + DBUG_RETURN(error); } else { + DBUG_PRINT("info",("Deleting offending row and trying to write new one again")); if ((error= table->file->ha_delete_row(table->record[1]))) { + DBUG_PRINT("info",("ha_delete_row() returns error %d",error)); table->file->print_error(error, MYF(0)); DBUG_RETURN(error); } @@ -7191,12 +7330,20 @@ replace_record(THD *thd, TABLE *table, DBUG_RETURN(error); } -int Write_rows_log_event::do_exec_row(TABLE *table) +#endif + +int +Write_rows_log_event::do_exec_row(const Relay_log_info *const rli) { - DBUG_ASSERT(table != NULL); - int error= replace_record(thd, table, m_master_reclength, m_width); - return error; + DBUG_ASSERT(m_table != NULL); + int error= write_row(rli, TRUE /* overwrite */); + + if (error && !thd->net.last_errno) + thd->net.last_errno= error; + + return error; } + #endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */ #ifdef MYSQL_CLIENT @@ -7289,40 +7436,52 @@ record_compare_exit: return result; } +/** + Locate the current row in event's 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. + The current row is pointed by @c m_curr_row. Member @c m_width tells how many + columns are there in the row (this can be differnet from the number of columns + in the table). It is assumed that event's table is already open and pointed + by @c m_table. - @param table Pointer to table to search - @param key Pointer to key to use for search, if table has key + If a corresponding record is found in the table it is stored in + @c m_table->record[0]. Note that when record is located based on a primary + key, it is possible that the record found differs from the row being located. - @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. + If no key is specified or table does not have keys, a table scan is used to + find the row. In that case the row should be complete and contain values for + all columns. However, it can still be shorter than the table, i.e. the table + can contain extra columns not present in the row. It is also possible that + the table has fewer columns than the row being located. - @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. + @returns Error code on failure, 0 on success. + + @post In case of success @c m_table->record[0] contains the record found. + Also, the internal "cursor" of the table is positioned at the record found. - @return Zero if the row was successfully fetched into - <code>table->record[1]</code>, error code otherwise. + @note If the engine allows random access of the records, a combination of + @c position() and @c rnd_pos() will be used. */ -static int find_and_fetch_row(TABLE *table, uchar *key) +int Rows_log_event::find_row(const Relay_log_info *rli) { - DBUG_ENTER("find_and_fetch_row(TABLE *table, uchar *key, uchar *record)"); - DBUG_PRINT("enter", ("table: 0x%lx, key: 0x%lx record: 0x%lx", - (long) table, (long) key, (long) table->record[1])); + DBUG_ENTER("find_row"); + + DBUG_ASSERT(m_table && m_table->in_use != NULL); + + TABLE *table= m_table; + int error; - DBUG_ASSERT(table->in_use != NULL); + /* unpack row - missing fields get default values */ + // TODO: shall we check and report errors here? + prepare_record(NULL,table,m_width,FALSE /* don't check errors */); + error= unpack_current_row(rli); + +#ifndef DBUG_OFF + DBUG_PRINT("info",("looking for the following record")); DBUG_DUMP("record[0]", table->record[0], table->s->reclength); +#endif if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) && table->s->primary_key < MAX_KEY) @@ -7345,34 +7504,53 @@ static int find_and_fetch_row(TABLE *table, uchar *key) table->s->reclength) == 0); */ - table->file->position(table->record[0]); - 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_PRINT("info",("locating record using primary key (position)")); + int error= table->file->rnd_pos_by_record(table->record[0]); + if (error) + { + DBUG_PRINT("info",("rnd_pos returns error %d",error)); + table->file->print_error(error, MYF(0)); + } DBUG_RETURN(error); } - /* We need to retrieve all fields */ - /* TODO: Move this out from this function to main loop */ + // We can't use position() - try other methods. + + /* + We need to retrieve all fields + TODO: Move this out from this function to main loop + */ table->use_all_columns(); + /* + Save copy of the record in table->record[1]. It might be needed + later if linear search is used to find exact match. + */ + store_record(table,record[1]); + if (table->s->keys > 0) { - int error; + DBUG_PRINT("info",("locating record using primary key (index_read)")); + /* We have a key: search the table using the index */ if (!table->file->inited && (error= table->file->ha_index_init(0, FALSE))) + { + DBUG_PRINT("info",("ha_index_init returns error %d",error)); + table->file->print_error(error, MYF(0)); DBUG_RETURN(error); + } - /* - Don't print debug messages when running valgrind since they can - trigger false warnings. - */ + /* Fill key data for the row */ + + DBUG_ASSERT(m_key); + key_copy(m_key, table->record[0], table->key_info, 0); + + /* + Don't print debug messages when running valgrind since they can + trigger false warnings. + */ #ifndef HAVE_purify - DBUG_DUMP("table->record[0]", table->record[0], table->s->reclength); - DBUG_DUMP("table->record[1]", table->record[1], table->s->reclength); + DBUG_DUMP("key data", m_key, table->key_info->key_length); #endif /* @@ -7383,10 +7561,13 @@ static int find_and_fetch_row(TABLE *table, uchar *key) */ my_ptrdiff_t const pos= table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0; - table->record[1][pos]= 0xFF; - if ((error= table->file->index_read(table->record[1], key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT))) + table->record[0][pos]= 0xFF; + + if ((error= table->file->index_read_map(table->record[0], m_key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT))) { + DBUG_PRINT("info",("no record matching the key found in the table")); table->file->print_error(error, MYF(0)); table->file->ha_index_end(); DBUG_RETURN(error); @@ -7397,8 +7578,8 @@ static int find_and_fetch_row(TABLE *table, uchar *key) trigger false warnings. */ #ifndef HAVE_purify - DBUG_DUMP("table->record[0]", table->record[0], table->s->reclength); - DBUG_DUMP("table->record[1]", table->record[1], table->s->reclength); + DBUG_PRINT("info",("found first matching record")); + DBUG_DUMP("record[0]", table->record[0], table->s->reclength); #endif /* Below is a minor "optimization". If the key (i.e., key number @@ -7420,10 +7601,15 @@ static int find_and_fetch_row(TABLE *table, uchar *key) DBUG_RETURN(0); } + /* + In case key is not unique, we still have to iterate over records found + and find the one which is identical to the row given. A copy of the + record we are looking for is stored in record[1]. + */ + DBUG_PRINT("info",("non-unique index, scanning it to find matching record")); + while (record_compare(table)) { - int error; - /* We need to set the null bytes to ensure that the filler bit are all set when returning. There are storage engines that @@ -7435,15 +7621,16 @@ static int find_and_fetch_row(TABLE *table, uchar *key) */ if (table->s->null_bytes > 0) { - table->record[1][table->s->null_bytes - 1]|= + table->record[0][table->s->null_bytes - 1]|= 256U - (1U << table->s->last_null_bit_pos); } - if ((error= table->file->index_next(table->record[1]))) + if ((error= table->file->index_next(table->record[0]))) { - table->file->print_error(error, MYF(0)); + DBUG_PRINT("info",("no record matching the given row found")); + table->file->print_error(error, MYF(0)); table->file->ha_index_end(); - DBUG_RETURN(error); + DBUG_RETURN(error); } } @@ -7454,44 +7641,57 @@ static int find_and_fetch_row(TABLE *table, uchar *key) } else { + DBUG_PRINT("info",("locating record using table scan (rnd_next)")); + int restart_count= 0; // Number of times scanning has restarted from top - int error; /* We don't have a key: search the table using rnd_next() */ if ((error= table->file->ha_rnd_init(1))) - return error; + { + DBUG_PRINT("info",("error initializing table scan" + " (ha_rnd_init returns %d)",error)); + table->file->print_error(error, MYF(0)); + DBUG_RETURN(error); + } /* Continue until we find the right record or have made a full loop */ do { - error= table->file->rnd_next(table->record[1]); - - DBUG_DUMP("record[0]", table->record[0], table->s->reclength); - DBUG_DUMP("record[1]", table->record[1], table->s->reclength); + error= table->file->rnd_next(table->record[0]); switch (error) { + case 0: case HA_ERR_RECORD_DELETED: - break; + break; case HA_ERR_END_OF_FILE: - if (++restart_count < 2) - table->file->ha_rnd_init(1); - break; + if (++restart_count < 2) + table->file->ha_rnd_init(1); + break; default: - table->file->print_error(error, MYF(0)); - DBUG_PRINT("info", ("Record not found")); + DBUG_PRINT("info", ("Failed to get next record" + " (rnd_next returns %d)",error)); + table->file->print_error(error, MYF(0)); table->file->ha_rnd_end(); - DBUG_RETURN(error); + DBUG_RETURN(error); } } while (restart_count < 2 && record_compare(table)); + + /* + Note: above record_compare will take into accout all record fields + which might be incorrect in case a partial row was given in the event + */ /* Have to restart the scan to be able to fetch the next row. */ - DBUG_PRINT("info", ("Record %sfound", restart_count == 2 ? "not " : "")); + if (restart_count == 2) + DBUG_PRINT("info", ("Record not found")); + else + DBUG_DUMP("record found", table->record[0], table->s->reclength); table->file->ha_rnd_end(); DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == 0); @@ -7500,6 +7700,7 @@ static int find_and_fetch_row(TABLE *table, uchar *key) DBUG_RETURN(0); } + #endif /* @@ -7511,9 +7712,6 @@ Delete_rows_log_event::Delete_rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid, MY_BITMAP const *cols, bool is_transactional) : Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional) -#ifdef HAVE_REPLICATION - ,m_memory(NULL), m_key(NULL), m_after_image(NULL) -#endif { } #endif /* #if !defined(MYSQL_CLIENT) */ @@ -7525,112 +7723,58 @@ Delete_rows_log_event::Delete_rows_log_event(THD *thd_arg, TABLE *tbl_arg, Delete_rows_log_event::Delete_rows_log_event(const char *buf, uint event_len, const Format_description_log_event *description_event) -#if defined(MYSQL_CLIENT) : Rows_log_event(buf, event_len, DELETE_ROWS_EVENT, description_event) -#else - : Rows_log_event(buf, event_len, DELETE_ROWS_EVENT, description_event), - m_memory(NULL), m_key(NULL), m_after_image(NULL) -#endif { } #endif #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) -int Delete_rows_log_event::do_before_row_operations(TABLE *table) -{ - DBUG_ASSERT(m_memory == NULL); - if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) && - table->s->primary_key < MAX_KEY) +int +Delete_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const) +{ + if ((m_table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) && + m_table->s->primary_key < MAX_KEY) { /* - We don't need to allocate any memory for m_after_image and - m_key since they are not used. + We don't need to allocate any memory for m_key since it is not used. */ return 0; } - int error= 0; - - if (table->s->keys > 0) - { - m_memory= (uchar*) my_multi_malloc(MYF(MY_WME), - &m_after_image, - (uint) table->s->reclength, - &m_key, - (uint) table->key_info->key_length, - NullS); - } - else + if (m_table->s->keys > 0) { - m_after_image= (uchar*) my_malloc(table->s->reclength, MYF(MY_WME)); - m_memory= (uchar*)m_after_image; - m_key= NULL; + // Allocate buffer for key searches + m_key= (uchar*)my_malloc(m_table->key_info->key_length, MYF(MY_WME)); + if (!m_key) + return HA_ERR_OUT_OF_MEM; } - if (!m_memory) - return HA_ERR_OUT_OF_MEM; - - return error; + return 0; } -int Delete_rows_log_event::do_after_row_operations(TABLE *table, int error) +int +Delete_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const, + int error) { /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/ - table->file->ha_index_or_rnd_end(); - my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR)); // Free for multi_malloc - m_memory= NULL; - m_after_image= NULL; + m_table->file->ha_index_or_rnd_end(); + my_free(m_key, MYF(MY_ALLOW_ZERO_PTR)); m_key= NULL; return error; } -int Delete_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO const *rli, - TABLE *table, - uchar const *const row_start, - uchar const **const row_end) -{ - DBUG_ASSERT(row_start && row_end); - /* - This assertion actually checks that there is at least as many - columns on the slave as on the master. - */ - DBUG_ASSERT(table->s->fields >= m_width); - - if (int error= unpack_row(rli, table, m_width, row_start, &m_cols, row_end, - &m_master_reclength, table->read_set, DELETE_ROWS_EVENT)) - { - thd->net.last_errno= error; - return error; - } - - /* - 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. - */ - if (m_key) - { - KEY *const key_info= table->key_info; - - key_copy(m_key, table->record[0], key_info, 0); - } - - return 0; -} - -int Delete_rows_log_event::do_exec_row(TABLE *table) +int Delete_rows_log_event::do_exec_row(const Relay_log_info *const rli) { int error; - DBUG_ASSERT(table != NULL); + DBUG_ASSERT(m_table != NULL); - if (!(error= find_and_fetch_row(table, m_key))) + if (!(error= find_row(rli))) { /* - Now we should have the right row to delete. We are using - record[0] since it is guaranteed to point to a record with the - correct value. + Delete the record found, located in record[0] */ - error= table->file->ha_delete_row(table->record[0]); + error= m_table->file->ha_delete_row(m_table->record[0]); } return error; } @@ -7660,10 +7804,6 @@ Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg, MY_BITMAP const *cols_ai, bool is_transactional) : Rows_log_event(thd_arg, tbl_arg, tid, cols_bi, is_transactional) -#ifdef HAVE_REPLICATION - , m_memory(NULL), m_key(NULL) - -#endif { init(cols_ai); } @@ -7673,24 +7813,24 @@ Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg, MY_BITMAP const *cols, bool is_transactional) : Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional) -#ifdef HAVE_REPLICATION - , m_memory(NULL), m_key(NULL) -#endif { init(cols); } void Update_rows_log_event::init(MY_BITMAP const *cols) { - /* if bitmap_init fails, catched in is_valid() */ + /* if bitmap_init fails, caught in is_valid() */ if (likely(!bitmap_init(&m_cols_ai, m_width <= sizeof(m_bitbuf_ai)*8 ? m_bitbuf_ai : NULL, - (m_width + 7) & ~7UL, + m_width, false))) { /* Cols can be zero if this is a dummy binrows event */ if (likely(cols != NULL)) + { memcpy(m_cols_ai.bitmap, cols->bitmap, no_bytes_in_map(cols)); + create_last_word_mask(&m_cols_ai); + } } } #endif /* !defined(MYSQL_CLIENT) */ @@ -7712,157 +7852,87 @@ Update_rows_log_event::Update_rows_log_event(const char *buf, uint event_len, const Format_description_log_event *description_event) -#if defined(MYSQL_CLIENT) : Rows_log_event(buf, event_len, UPDATE_ROWS_EVENT, description_event) -#else - : Rows_log_event(buf, event_len, UPDATE_ROWS_EVENT, description_event), - m_memory(NULL), m_key(NULL) -#endif { } #endif #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) -int Update_rows_log_event::do_before_row_operations(TABLE *table) -{ - DBUG_ASSERT(m_memory == NULL); - - int error= 0; - if (table->s->keys > 0) - { - m_memory= (uchar*) my_multi_malloc(MYF(MY_WME), - &m_after_image, - (uint) table->s->reclength, - &m_key, - (uint) table->key_info->key_length, - NullS); - } - else +int +Update_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const) +{ + if (m_table->s->keys > 0) { - m_after_image= (uchar*) my_malloc(table->s->reclength, MYF(MY_WME)); - m_memory= m_after_image; - m_key= NULL; + // Allocate buffer for key searches + m_key= (uchar*)my_malloc(m_table->key_info->key_length, MYF(MY_WME)); + if (!m_key) + return HA_ERR_OUT_OF_MEM; } - if (!m_memory) - return HA_ERR_OUT_OF_MEM; - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; + m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - return error; + return 0; } -int Update_rows_log_event::do_after_row_operations(TABLE *table, int error) +int +Update_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const, + int error) { /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/ - table->file->ha_index_or_rnd_end(); - my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR)); - m_memory= NULL; - m_after_image= NULL; + m_table->file->ha_index_or_rnd_end(); + my_free(m_key, MYF(MY_ALLOW_ZERO_PTR)); // Free for multi_malloc m_key= NULL; return error; } -int Update_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO const *rli, - TABLE *table, - uchar const *const row_start, - uchar const **const row_end) +int +Update_rows_log_event::do_exec_row(const Relay_log_info *const rli) { - int error; - DBUG_ASSERT(row_start && row_end); - /* - This assertion actually checks that there is at least as many - columns on the slave as on the master. - */ - DBUG_ASSERT(table->s->fields >= m_width); + DBUG_ASSERT(m_table != NULL); - /* - 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 */ - if ((error= unpack_row(rli, table, m_width, row_start, &m_cols, row_end, - &m_master_reclength, table->read_set, UPDATE_ROWS_EVENT))) - { - thd->net.last_errno= error; - return error; - } - - store_record(table, record[1]); - uchar const *next_start = *row_end; - /* m_after_image is the after image for the update */ - if ((error= unpack_row(rli, table, m_width, next_start, &m_cols_ai, row_end, - &m_master_reclength, table->write_set, UPDATE_ROWS_EVENT))) - { - thd->net.last_errno= error; + int error= find_row(rli); + if (error) return error; - } - - 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]", table->record[0], table->s->reclength); - DBUG_DUMP("m_after_image", m_after_image, table->s->reclength); -#endif + This is the situation after locating BI: - /* - 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. - */ - if (m_key) - { - KEY *const key_info= table->key_info; + ===|=== before image ====|=== after image ===|=== + ^ ^ + m_curr_row m_curr_row_end - key_copy(m_key, table->record[0], key_info, 0); - } - - return error; -} - -int Update_rows_log_event::do_exec_row(TABLE *table) -{ - DBUG_ASSERT(table != NULL); + BI found in the table is stored in record[0]. We copy it to record[1] + and unpack AI to record[0]. + */ - int error= find_and_fetch_row(table, m_key); - if (error) - return error; + store_record(m_table,record[1]); - /* - We have to ensure that the new record (i.e., the after image) is - in record[0] and the old record (i.e., the before image) is in - record[1]. This since some storage engines require this (for - example, the partition engine). - - Since find_and_fetch_row() puts the fetched record (i.e., the old - record) in record[1], we can keep it there. We put the new record - (i.e., the after image) into record[0], and copy the fields that - are on the slave (i.e., in record[1]) into record[0], effectively - overwriting the default values that where put there by the - unpack_row() function. - */ - bmove_align(table->record[0], m_after_image, table->s->reclength); - copy_extra_record_fields(table, m_master_reclength, m_width); + m_curr_row= m_curr_row_end; + error= unpack_current_row(rli); // this also updates m_curr_row_end /* Now we have the right row to update. The old row (the one we're - looking for) is in record[1] and the new row has is in record[0]. - We also have copied the original values already in the slave's - database into the after image delivered from the master. + looking for) is in record[1] and the new row is in record[0]. */ - error= table->file->ha_update_row(table->record[1], table->record[0]); +#ifndef HAVE_purify + /* + Don't print debug messages when running valgrind since they can + trigger false warnings. + */ + DBUG_PRINT("info",("Updating row in table")); + DBUG_DUMP("old record", m_table->record[1], m_table->s->reclength); + DBUG_DUMP("new values", m_table->record[0], m_table->s->reclength); +#endif + + error= m_table->file->ha_update_row(m_table->record[1], m_table->record[0]); if (error == HA_ERR_RECORD_IS_THE_SAME) error= 0; return error; } + #endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */ #ifdef MYSQL_CLIENT @@ -7954,7 +8024,7 @@ Incident_log_event::print(FILE *file, #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) int -Incident_log_event::do_apply_event(RELAY_LOG_INFO const *rli) +Incident_log_event::do_apply_event(Relay_log_info const *rli) { DBUG_ENTER("Incident_log_event::do_apply_event"); rli->report(ERROR_LEVEL, ER_SLAVE_INCIDENT, @@ -7981,5 +8051,3 @@ Incident_log_event::write_data_body(IO_CACHE *file) DBUG_ENTER("Incident_log_event::write_data_body"); DBUG_RETURN(write_str(file, m_message.str, m_message.length)); } - - diff --git a/sql/log_event.h b/sql/log_event.h index 6787aa08016..0c66d1b190f 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -13,6 +13,15 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/** + @addtogroup Replication + @{ + + @file + + Binary log event definitions. +*/ + #ifndef _log_event_h #define _log_event_h @@ -23,6 +32,10 @@ #include <my_bitmap.h> #include "rpl_constants.h" +#ifndef MYSQL_CLIENT +#include "rpl_record.h" +#include "rpl_reporting.h" +#endif #define LOG_READ_EOF -1 #define LOG_READ_BOGUS -2 @@ -57,8 +70,8 @@ which increments every time we write an event to the binlog) (3 bytes). Q: how do we handle when the counter is overflowed and restarts from 0 ? - - Query and Load (Create or Execute) events may have a more precise timestamp - (with microseconds), number of matched/affected/warnings rows + - Query and Load (Create or Execute) events may have a more precise + timestamp (with microseconds), number of matched/affected/warnings rows and fields of session variables: SQL_MODE, FOREIGN_KEY_CHECKS, UNIQUE_CHECKS, SQL_AUTO_IS_NULL, the collations and charsets, the PASSWORD() version (old/new/...). @@ -520,9 +533,7 @@ class THD; #endif class Format_description_log_event; - -struct st_relay_log_info; -typedef st_relay_log_info RELAY_LOG_INFO; +class Relay_log_info; #ifdef MYSQL_CLIENT /* @@ -708,7 +719,8 @@ public: */ static Log_event* read_log_event(IO_CACHE* file, pthread_mutex_t* log_lock, - const Format_description_log_event *description_event); + const Format_description_log_event + *description_event); static int read_log_event(IO_CACHE* file, String* packet, pthread_mutex_t* log_lock); /* @@ -736,7 +748,8 @@ public: Log_event() : temp_buf(0) {} /* avoid having to link mysqlbinlog against libpthread */ static Log_event* read_log_event(IO_CACHE* file, - const Format_description_log_event *description_event); + const Format_description_log_event + *description_event); /* print*() functions are used by mysqlbinlog */ virtual void print(FILE* file, PRINT_EVENT_INFO* print_event_info) = 0; void print_timestamp(IO_CACHE* file, time_t *ts = 0); @@ -772,12 +785,24 @@ public: { return 0; } virtual bool write_data_body(IO_CACHE* file __attribute__((unused))) { return 0; } + inline time_t get_time() + { + THD *tmp_thd; + if (when) + return when; + if (thd) + return thd->start_time; + if ((tmp_thd= current_thd)) + return tmp_thd->start_time; + return my_time(0); + } #endif virtual Log_event_type get_type_code() = 0; virtual bool is_valid() const = 0; virtual bool is_artificial_event() { return 0; } inline bool get_cache_stmt() const { return cache_stmt; } - Log_event(const char* buf, const Format_description_log_event* description_event); + Log_event(const char* buf, const Format_description_log_event + *description_event); virtual ~Log_event() { free_temp_buf();} void register_temp_buf(char* buf) { temp_buf = buf; } void free_temp_buf() @@ -800,6 +825,8 @@ public: /* returns the human readable name of the event's type */ const char* get_type_str(); + /* Return start of query time or current time */ + #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) public: @@ -811,7 +838,8 @@ public: @see do_apply_event */ - int apply_event(RELAY_LOG_INFO const *rli) { + int apply_event(Relay_log_info const *rli) + { return do_apply_event(rli); } @@ -824,7 +852,7 @@ public: @see do_update_pos */ - int update_pos(RELAY_LOG_INFO *rli) + int update_pos(Relay_log_info *rli) { return do_update_pos(rli); } @@ -835,7 +863,7 @@ public: @see do_shall_skip */ - enum_skip_reason shall_skip(RELAY_LOG_INFO *rli) + enum_skip_reason shall_skip(Relay_log_info *rli) { return do_shall_skip(rli); } @@ -857,7 +885,7 @@ protected: @retval 0 Event applied successfully @retval errno Error code if event application failed */ - virtual int do_apply_event(RELAY_LOG_INFO const *rli) + virtual int do_apply_event(Relay_log_info const *rli) { return 0; /* Default implementation does nothing */ } @@ -886,7 +914,7 @@ protected: 1). Observe that handler errors are returned by the do_apply_event() function, and not by this one. */ - virtual int do_update_pos(RELAY_LOG_INFO *rli); + virtual int do_update_pos(Relay_log_info *rli); /** @@ -918,8 +946,7 @@ protected: The event shall be skipped because the slave skip counter was non-zero. The caller shall decrease the counter by one. */ - virtual enum_skip_reason do_shall_skip(RELAY_LOG_INFO *rli); - + virtual enum_skip_reason do_shall_skip(Relay_log_info *rli); #endif }; @@ -1059,10 +1086,10 @@ public: public: /* !!! Public in this patch to allow old usage */ #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_apply_event(RELAY_LOG_INFO const *rli); - virtual int do_update_pos(RELAY_LOG_INFO *rli); + virtual int do_apply_event(Relay_log_info const *rli); + virtual int do_update_pos(Relay_log_info *rli); - int do_apply_event(RELAY_LOG_INFO const *rli, + int do_apply_event(Relay_log_info const *rli, const char *query_arg, uint32 q_len_arg); #endif /* HAVE_REPLICATION */ @@ -1114,7 +1141,7 @@ public: uint16 master_port; #ifndef MYSQL_CLIENT - Slave_log_event(THD* thd_arg, RELAY_LOG_INFO* rli); + Slave_log_event(THD* thd_arg, Relay_log_info* rli); void pack_info(Protocol* protocol); #else void print(FILE* file, PRINT_EVENT_INFO* print_event_info); @@ -1131,7 +1158,7 @@ public: private: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_apply_event(RELAY_LOG_INFO const* rli); + virtual int do_apply_event(Relay_log_info const* rli); #endif }; @@ -1151,7 +1178,8 @@ private: char **fn_start, char **fn_end); protected: int copy_log_event(const char *buf, ulong event_len, - int body_offset, const Format_description_log_event* description_event); + int body_offset, + const Format_description_log_event* description_event); public: ulong thread_id; @@ -1236,12 +1264,12 @@ public: public: /* !!! Public in this patch to allow old usage */ #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_apply_event(RELAY_LOG_INFO const* rli) + virtual int do_apply_event(Relay_log_info const* rli) { return do_apply_event(thd->slave_net,rli,0); } - int do_apply_event(NET *net, RELAY_LOG_INFO const *rli, + int do_apply_event(NET *net, Relay_log_info const *rli, bool use_rli_only_for_errors); #endif }; @@ -1296,6 +1324,11 @@ public: setting log_event == 0 (for now). */ bool artificial_event; + /* + We set this to 1 if we don't want to have the created time in the log, + which is the case when we rollover to a new log. + */ + bool dont_set_created; #ifndef MYSQL_CLIENT Start_log_event_v3(); @@ -1323,8 +1356,8 @@ public: protected: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_apply_event(RELAY_LOG_INFO const *rli); - virtual enum_skip_reason do_shall_skip(RELAY_LOG_INFO*) + virtual int do_apply_event(Relay_log_info const *rli); + virtual enum_skip_reason do_shall_skip(Relay_log_info*) { /* Events from ourself should be skipped, but they should not @@ -1362,7 +1395,8 @@ public: Format_description_log_event(uint8 binlog_ver, const char* server_ver=0); Format_description_log_event(const char* buf, uint event_len, - const Format_description_log_event* description_event); + const Format_description_log_event + *description_event); ~Format_description_log_event() { my_free((uchar*)post_header_len, MYF(0)); } Log_event_type get_type_code() { return FORMAT_DESCRIPTION_EVENT;} #ifndef MYSQL_CLIENT @@ -1388,9 +1422,9 @@ public: protected: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_apply_event(RELAY_LOG_INFO const *rli); - virtual int do_update_pos(RELAY_LOG_INFO *rli); - virtual enum_skip_reason do_shall_skip(RELAY_LOG_INFO *rli); + virtual int do_apply_event(Relay_log_info const *rli); + virtual int do_update_pos(Relay_log_info *rli); + virtual enum_skip_reason do_shall_skip(Relay_log_info *rli); #endif }; @@ -1420,7 +1454,8 @@ public: void print(FILE* file, PRINT_EVENT_INFO* print_event_info); #endif - Intvar_log_event(const char* buf, const Format_description_log_event* description_event); + Intvar_log_event(const char* buf, + const Format_description_log_event *description_event); ~Intvar_log_event() {} Log_event_type get_type_code() { return INTVAR_EVENT;} const char* get_var_type_name(); @@ -1432,9 +1467,9 @@ public: private: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_apply_event(RELAY_LOG_INFO const *rli); - virtual int do_update_pos(RELAY_LOG_INFO *rli); - virtual enum_skip_reason do_shall_skip(RELAY_LOG_INFO *rli); + virtual int do_apply_event(Relay_log_info const *rli); + virtual int do_update_pos(Relay_log_info *rli); + virtual enum_skip_reason do_shall_skip(Relay_log_info *rli); #endif }; @@ -1467,7 +1502,8 @@ class Rand_log_event: public Log_event void print(FILE* file, PRINT_EVENT_INFO* print_event_info); #endif - Rand_log_event(const char* buf, const Format_description_log_event* description_event); + Rand_log_event(const char* buf, + const Format_description_log_event *description_event); ~Rand_log_event() {} Log_event_type get_type_code() { return RAND_EVENT;} int get_data_size() { return 16; /* sizeof(ulonglong) * 2*/ } @@ -1478,9 +1514,9 @@ class Rand_log_event: public Log_event private: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_apply_event(RELAY_LOG_INFO const *rli); - virtual int do_update_pos(RELAY_LOG_INFO *rli); - virtual enum_skip_reason do_shall_skip(RELAY_LOG_INFO *rli); + virtual int do_apply_event(Relay_log_info const *rli); + virtual int do_update_pos(Relay_log_info *rli); + virtual enum_skip_reason do_shall_skip(Relay_log_info *rli); #endif }; @@ -1510,7 +1546,8 @@ class Xid_log_event: public Log_event void print(FILE* file, PRINT_EVENT_INFO* print_event_info); #endif - Xid_log_event(const char* buf, const Format_description_log_event* description_event); + Xid_log_event(const char* buf, + const Format_description_log_event *description_event); ~Xid_log_event() {} Log_event_type get_type_code() { return XID_EVENT;} int get_data_size() { return sizeof(xid); } @@ -1521,7 +1558,7 @@ class Xid_log_event: public Log_event private: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_apply_event(RELAY_LOG_INFO const *rli); + virtual int do_apply_event(Relay_log_info const *rli); #endif }; @@ -1556,7 +1593,8 @@ public: void print(FILE* file, PRINT_EVENT_INFO* print_event_info); #endif - User_var_log_event(const char* buf, const Format_description_log_event* description_event); + User_var_log_event(const char* buf, + const Format_description_log_event *description_event); ~User_var_log_event() {} Log_event_type get_type_code() { return USER_VAR_EVENT;} #ifndef MYSQL_CLIENT @@ -1566,9 +1604,9 @@ public: private: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_apply_event(RELAY_LOG_INFO const *rli); - virtual int do_update_pos(RELAY_LOG_INFO *rli); - virtual enum_skip_reason do_shall_skip(RELAY_LOG_INFO *rli); + virtual int do_apply_event(Relay_log_info const *rli); + virtual int do_update_pos(Relay_log_info *rli); + virtual enum_skip_reason do_shall_skip(Relay_log_info *rli); #endif }; @@ -1588,7 +1626,8 @@ public: void print(FILE* file, PRINT_EVENT_INFO* print_event_info); #endif - Stop_log_event(const char* buf, const Format_description_log_event* description_event): + Stop_log_event(const char* buf, + const Format_description_log_event *description_event): Log_event(buf, description_event) {} ~Stop_log_event() {} @@ -1597,8 +1636,8 @@ public: private: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_update_pos(RELAY_LOG_INFO *rli); - virtual enum_skip_reason do_shall_skip(RELAY_LOG_INFO *rli) + virtual int do_update_pos(Relay_log_info *rli); + virtual enum_skip_reason do_shall_skip(Relay_log_info *rli) { /* Events from ourself should be skipped, but they should not @@ -1657,8 +1696,8 @@ public: private: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_update_pos(RELAY_LOG_INFO *rli); - virtual enum_skip_reason do_shall_skip(RELAY_LOG_INFO *rli); + virtual int do_update_pos(Relay_log_info *rli); + virtual enum_skip_reason do_shall_skip(Relay_log_info *rli); #endif }; @@ -1697,7 +1736,8 @@ public: #endif /* HAVE_REPLICATION */ #else void print(FILE* file, PRINT_EVENT_INFO* print_event_info); - void print(FILE* file, PRINT_EVENT_INFO* print_event_info, bool enable_local); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info, + bool enable_local); #endif Create_file_log_event(const char* buf, uint event_len, @@ -1730,7 +1770,7 @@ public: private: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_apply_event(RELAY_LOG_INFO const *rli); + virtual int do_apply_event(Relay_log_info const *rli); #endif }; @@ -1772,7 +1812,8 @@ public: #endif Append_block_log_event(const char* buf, uint event_len, - const Format_description_log_event* description_event); + const Format_description_log_event + *description_event); ~Append_block_log_event() {} Log_event_type get_type_code() { return APPEND_BLOCK_EVENT;} int get_data_size() { return block_len + APPEND_BLOCK_HEADER_LEN ;} @@ -1784,7 +1825,7 @@ public: private: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_apply_event(RELAY_LOG_INFO const *rli); + virtual int do_apply_event(Relay_log_info const *rli); #endif }; @@ -1808,7 +1849,8 @@ public: #endif /* HAVE_REPLICATION */ #else void print(FILE* file, PRINT_EVENT_INFO* print_event_info); - void print(FILE* file, PRINT_EVENT_INFO* print_event_info, bool enable_local); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info, + bool enable_local); #endif Delete_file_log_event(const char* buf, uint event_len, @@ -1824,7 +1866,7 @@ public: private: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_apply_event(RELAY_LOG_INFO const *rli); + virtual int do_apply_event(Relay_log_info const *rli); #endif }; @@ -1851,7 +1893,8 @@ public: #endif Execute_load_log_event(const char* buf, uint event_len, - const Format_description_log_event* description_event); + const Format_description_log_event + *description_event); ~Execute_load_log_event() {} Log_event_type get_type_code() { return EXEC_LOAD_EVENT;} int get_data_size() { return EXEC_LOAD_HEADER_LEN ;} @@ -1863,7 +1906,7 @@ public: private: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_apply_event(RELAY_LOG_INFO const *rli); + virtual int do_apply_event(Relay_log_info const *rli); #endif }; @@ -1890,7 +1933,8 @@ public: #endif /* HAVE_REPLICATION */ #endif Begin_load_query_log_event(const char* buf, uint event_len, - const Format_description_log_event* description_event); + const Format_description_log_event + *description_event); ~Begin_load_query_log_event() {} Log_event_type get_type_code() { return BEGIN_LOAD_QUERY_EVENT; } }; @@ -1942,7 +1986,8 @@ public: const char *local_fname); #endif Execute_load_query_log_event(const char* buf, uint event_len, - const Format_description_log_event *description_event); + const Format_description_log_event + *description_event); ~Execute_load_query_log_event() {} Log_event_type get_type_code() { return EXECUTE_LOAD_QUERY_EVENT; } @@ -1955,7 +2000,7 @@ public: private: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_apply_event(RELAY_LOG_INFO const *rli); + virtual int do_apply_event(Relay_log_info const *rli); #endif }; @@ -1969,7 +2014,8 @@ public: Log_event's ctor, this way we can extract maximum information from the event's header (the unique ID for example). */ - Unknown_log_event(const char* buf, const Format_description_log_event* description_event): + Unknown_log_event(const char* buf, + const Format_description_log_event *description_event): Log_event(buf, description_event) {} ~Unknown_log_event() {} @@ -2049,6 +2095,7 @@ public: virtual int get_data_size() { return m_data_size; } #ifndef MYSQL_CLIENT + virtual int save_field_metadata(); virtual bool write_data_header(IO_CACHE *file); virtual bool write_data_body(IO_CACHE *file); virtual const char *get_db() { return m_dbnam; } @@ -2065,9 +2112,9 @@ public: private: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_apply_event(RELAY_LOG_INFO const *rli); - virtual int do_update_pos(RELAY_LOG_INFO *rli); - virtual enum_skip_reason do_shall_skip(RELAY_LOG_INFO *rli); + virtual int do_apply_event(Relay_log_info const *rli); + virtual int do_update_pos(Relay_log_info *rli); + virtual enum_skip_reason do_shall_skip(Relay_log_info *rli); #endif #ifndef MYSQL_CLIENT @@ -2085,6 +2132,14 @@ private: flag_set m_flags; size_t m_data_size; + + uchar *m_field_metadata; // buffer for field metadata + /* + The size of field metadata buffer set by calling save_field_metadata() + */ + ulong m_field_metadata_size; + uchar *m_null_bits; + uchar *m_meta_memory; }; @@ -2135,7 +2190,13 @@ public: NO_FOREIGN_KEY_CHECKS_F = (1U << 1), /* Value of the OPTION_RELAXED_UNIQUE_CHECKS flag in thd->options */ - RELAXED_UNIQUE_CHECKS_F = (1U << 2) + RELAXED_UNIQUE_CHECKS_F = (1U << 2), + + /** + Indicates that rows in this event are complete, that is contain + values for all columns of the table. + */ + COMPLETE_ROWS_F = (1U << 3) }; typedef uint16 flag_set; @@ -2239,14 +2300,33 @@ protected: uchar *m_rows_cur; /* One-after the end of the data */ uchar *m_rows_end; /* One-after the end of the allocated space */ - flag_set m_flags; /* Flags for row-level events */ + flag_set m_flags; /* Flags for row-level events */ + + /* helper functions */ + +#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) + const uchar *m_curr_row; /* Start of the row being processed */ + const uchar *m_curr_row_end; /* One-after the end of the current row */ + uchar *m_key; /* Buffer to keep key value during searches */ + + int find_row(const Relay_log_info *const); + int write_row(const Relay_log_info *const, const bool); + + // Unpack the current row into m_table->record[0] + int unpack_current_row(const Relay_log_info *const rli) + { + DBUG_ASSERT(m_table); + return ::unpack_row(rli, m_table, m_width, m_curr_row, &m_cols, + &m_curr_row_end, &m_master_reclength); + } +#endif private: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_apply_event(RELAY_LOG_INFO const *rli); - virtual int do_update_pos(RELAY_LOG_INFO *rli); - virtual enum_skip_reason do_shall_skip(RELAY_LOG_INFO *rli); + virtual int do_apply_event(Relay_log_info const *rli); + virtual int do_update_pos(Relay_log_info *rli); + virtual enum_skip_reason do_shall_skip(Relay_log_info *rli); /* Primitive to prepare for a sequence of row executions. @@ -2264,7 +2344,8 @@ private: The member function will return 0 if all went OK, or a non-zero error code otherwise. */ - virtual int do_before_row_operations(TABLE *table) = 0; + virtual + int do_before_row_operations(const Slave_reporting_capability *const log) = 0; /* Primitive to clean up after a sequence of row executions. @@ -2274,45 +2355,33 @@ private: After doing a sequence of do_prepare_row() and do_exec_row(), this member function should be called to clean up and release any allocated buffers. + + The error argument, if non-zero, indicates an error which happened during + row processing before this function was called. In this case, even if + function is successful, it should return the error code given in the argument. */ - virtual int do_after_row_operations(TABLE *table, int error) = 0; - - /* - Primitive to prepare for handling one row in a row-level event. - - DESCRIPTION - - The member function prepares for execution of operations needed for one - row in a row-level event by reading up data from the buffer containing - the row. No specific interpretation of the data is normally done here, - since SQL thread specific data is not available: that data is made - available for the do_exec function. - - A pointer to the start of the next row, or NULL if the preparation - failed. Currently, preparation cannot fail, but don't rely on this - behavior. - - RETURN VALUE - Error code, if something went wrong, 0 otherwise. - */ - virtual int do_prepare_row(THD*, RELAY_LOG_INFO const*, TABLE*, - uchar const *row_start, - uchar const **row_end) = 0; + virtual + int do_after_row_operations(const Slave_reporting_capability *const log, + int error) = 0; /* Primitive to do the actual execution necessary for a row. DESCRIPTION The member function will do the actual execution needed to handle a row. + The row is located at m_curr_row. When the function returns, + m_curr_row_end should point at the next row (one byte after the end + of the current row). RETURN VALUE 0 if execution succeeded, 1 if execution failed. */ - virtual int do_exec_row(TABLE *table) = 0; + virtual int do_exec_row(const Relay_log_info *const rli) = 0; #endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */ -}; + friend class Old_rows_log_event; +}; /***************************************************************************** @@ -2362,14 +2431,9 @@ private: #endif #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - uchar *m_memory; - uchar *m_after_image; - - virtual int do_before_row_operations(TABLE *table); - virtual int do_after_row_operations(TABLE *table, int error); - virtual int do_prepare_row(THD*, RELAY_LOG_INFO const*, TABLE*, - uchar const *row_start, uchar const **row_end); - virtual int do_exec_row(TABLE *table); + virtual int do_before_row_operations(const Slave_reporting_capability *const); + virtual int do_after_row_operations(const Slave_reporting_capability *const,int); + virtual int do_exec_row(const Relay_log_info *const); #endif }; @@ -2441,15 +2505,9 @@ protected: #endif #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - uchar *m_memory; - uchar *m_key; - uchar *m_after_image; - - virtual int do_before_row_operations(TABLE *table); - virtual int do_after_row_operations(TABLE *table, int error); - virtual int do_prepare_row(THD*, RELAY_LOG_INFO const*, TABLE*, - uchar const *row_start, uchar const **row_end); - virtual int do_exec_row(TABLE *table); + virtual int do_before_row_operations(const Slave_reporting_capability *const); + virtual int do_after_row_operations(const Slave_reporting_capability *const,int); + virtual int do_exec_row(const Relay_log_info *const); #endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */ }; @@ -2512,15 +2570,9 @@ protected: #endif #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - uchar *m_memory; - uchar *m_key; - uchar *m_after_image; - - virtual int do_before_row_operations(TABLE *table); - virtual int do_after_row_operations(TABLE *table, int error); - virtual int do_prepare_row(THD*, RELAY_LOG_INFO const*, TABLE*, - uchar const *row_start, uchar const **row_end); - virtual int do_exec_row(TABLE *table); + virtual int do_before_row_operations(const Slave_reporting_capability *const); + virtual int do_after_row_operations(const Slave_reporting_capability *const,int); + virtual int do_exec_row(const Relay_log_info *const); #endif }; @@ -2539,7 +2591,7 @@ protected: <caption>Incident event format</caption> <tr> <th>Symbol</th> - <th>Size<br/>(bytes)</th> + <th>Size<br>(bytes)</th> <th>Description</th> </tr> <tr> @@ -2596,7 +2648,7 @@ public: #endif #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_apply_event(RELAY_LOG_INFO const *rli); + virtual int do_apply_event(Relay_log_info const *rli); #endif virtual bool write_data_header(IO_CACHE *file); @@ -2624,4 +2676,8 @@ static inline bool copy_event_cache_to_file_and_reinit(IO_CACHE *cache, reinit_io_cache(cache, WRITE_CACHE, 0, FALSE, TRUE); } +/** + @} (end of group Replication) +*/ + #endif /* _log_event_h */ diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index e6a9dd50c08..949179386ea 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1,12 +1,970 @@ #include "mysql_priv.h" +#ifndef MYSQL_CLIENT +#include "rpl_rli.h" +#include "rpl_utility.h" +#endif #include "log_event_old.h" #include "rpl_record_old.h" #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) + +// Old implementation of do_apply_event() +int +Old_rows_log_event::do_apply_event(Rows_log_event *ev, const Relay_log_info *rli) +{ + DBUG_ENTER("Rows_log_event::do_apply_event(st_relay_log_info*)"); + int error= 0; + THD *thd= ev->thd; + uchar const *row_start= ev->m_rows_buf; + + /* + If m_table_id == ~0UL, then we have a dummy event that does not + contain any data. In that case, we just remove all tables in the + tables_to_lock list, close the thread tables, and return with + success. + */ + if (ev->m_table_id == ~0UL) + { + /* + This one is supposed to be set: just an extra check so that + nothing strange has happened. + */ + DBUG_ASSERT(ev->get_flags(Rows_log_event::STMT_END_F)); + + const_cast<Relay_log_info*>(rli)->clear_tables_to_lock(); + close_thread_tables(thd); + thd->clear_error(); + DBUG_RETURN(0); + } + + /* + 'thd' has been set by exec_relay_log_event(), just before calling + do_apply_event(). We still check here to prevent future coding + errors. + */ + DBUG_ASSERT(rli->sql_thd == thd); + + /* + If there is no locks taken, this is the first binrow event seen + after the table map events. We should then lock all the tables + used in the transaction and proceed with execution of the actual + event. + */ + if (!thd->lock) + { + bool need_reopen= 1; /* To execute the first lap of the loop below */ + + /* + lock_tables() reads the contents of thd->lex, so they must be + initialized. Contrary to in + Table_map_log_event::do_apply_event() we don't call + mysql_init_query() as that may reset the binlog format. + */ + lex_start(thd); + + while ((error= lock_tables(thd, rli->tables_to_lock, + rli->tables_to_lock_count, &need_reopen))) + { + if (!need_reopen) + { + 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; + rli->report(ERROR_LEVEL, actual_error, + "Error '%s' in %s event: when locking tables", + (actual_error ? thd->net.last_error : + "unexpected success or fatal error"), + ev->get_type_str()); + thd->is_fatal_error= 1; + } + else + { + rli->report(ERROR_LEVEL, error, + "Error in %s event: when locking tables", + ev->get_type_str()); + } + const_cast<Relay_log_info*>(rli)->clear_tables_to_lock(); + DBUG_RETURN(error); + } + + /* + So we need to reopen the tables. + + We need to flush the pending RBR event, since it keeps a + pointer to an open table. + + ALTERNATIVE SOLUTION (not implemented): Extract a pointer to + the pending RBR event and reset the table pointer after the + tables has been reopened. + + NOTE: For this new scheme there should be no pending event: + need to add code to assert that is the case. + */ + thd->binlog_flush_pending_rows_event(false); + TABLE_LIST *tables= rli->tables_to_lock; + close_tables_for_reopen(thd, &tables); + + 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) + { + /* + 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; + rli->report(ERROR_LEVEL, actual_error, + "Error '%s' on reopening tables", + (actual_error ? thd->net.last_error : + "unexpected success or fatal error")); + thd->query_error= 1; + } + const_cast<Relay_log_info*>(rli)->clear_tables_to_lock(); + 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; + const_cast<Relay_log_info*>(rli)->clear_tables_to_lock(); + DBUG_RETURN(Rows_log_event::ERR_BAD_TABLE_DEF); + } + } + } + + /* + ... 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. + */ + 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); + } +#ifdef HAVE_QUERY_CACHE + query_cache.invalidate_locked_for_write(rli->tables_to_lock); +#endif + } + + TABLE* table= const_cast<Relay_log_info*>(rli)->m_table_map.get_table(ev->m_table_id); + + if (table) + { + /* + table == NULL means that this table should not be replicated + (this was set up by Table_map_log_event::do_apply_event() + which tested replicate-* rules). + */ + + /* + It's not needed to set_time() but + 1) it continues the property that "Time" in SHOW PROCESSLIST shows how + much slave is behind + 2) it will be needed when we allow replication from a table with no + TIMESTAMP column to a table with one. + So we call set_time(), like in SBR. Presently it changes nothing. + */ + thd->set_time((time_t)ev->when); + /* + There are a few flags that are replicated with each row event. + Make sure to set/clear them before executing the main body of + the event. + */ + if (ev->get_flags(Rows_log_event::NO_FOREIGN_KEY_CHECKS_F)) + thd->options|= OPTION_NO_FOREIGN_KEY_CHECKS; + else + thd->options&= ~OPTION_NO_FOREIGN_KEY_CHECKS; + + if (ev->get_flags(Rows_log_event::RELAXED_UNIQUE_CHECKS_F)) + thd->options|= OPTION_RELAXED_UNIQUE_CHECKS; + else + thd->options&= ~OPTION_RELAXED_UNIQUE_CHECKS; + /* A small test to verify that objects have consistent types */ + DBUG_ASSERT(sizeof(thd->options) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS)); + + /* + Now we are in a statement and will stay in a statement until we + see a STMT_END_F. + + We set this flag here, before actually applying any rows, in + case the SQL thread is stopped and we need to detect that we're + inside a statement and halting abruptly might cause problems + when restarting. + */ + const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT); + + error= do_before_row_operations(table); + while (error == 0 && row_start < ev->m_rows_end) + { + uchar const *row_end= NULL; + 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 <= ev->m_rows_end); + + /* in_use can have been set to NULL in close_tables_for_reopen */ + THD* old_thd= table->in_use; + if (!table->in_use) + table->in_use= thd; + error= do_exec_row(table); + table->in_use = old_thd; + switch (error) + { + /* Some recoverable errors */ + case HA_ERR_RECORD_CHANGED: + case HA_ERR_KEY_NOT_FOUND: /* Idempotency support: OK if + tuple does not exist */ + error= 0; + case 0: + break; + + default: + rli->report(ERROR_LEVEL, thd->net.last_errno, + "Error in %s event: row application failed. %s", + ev->get_type_str(), + thd->net.last_error ? thd->net.last_error : ""); + thd->query_error= 1; + break; + } + + row_start= row_end; + } + DBUG_EXECUTE_IF("STOP_SLAVE_after_first_Rows_event", + const_cast<Relay_log_info*>(rli)->abort_slave= 1;); + error= do_after_row_operations(table, error); + if (!ev->cache_stmt) + { + DBUG_PRINT("info", ("Marked that we need to keep log")); + thd->options|= OPTION_KEEP_LOG; + } + } + + /* + We need to delay this clear until the table def is no longer needed. + The table def is needed in unpack_row(). + */ + if (rli->tables_to_lock && ev->get_flags(Rows_log_event::STMT_END_F)) + const_cast<Relay_log_info*>(rli)->clear_tables_to_lock(); + + if (error) + { /* error has occured during the transaction */ + rli->report(ERROR_LEVEL, thd->net.last_errno, + "Error in %s event: error during transaction execution " + "on table %s.%s. %s", + ev->get_type_str(), table->s->db.str, + table->s->table_name.str, + thd->net.last_error ? thd->net.last_error : ""); + + /* + If one day we honour --skip-slave-errors in row-based replication, and + the error should be skipped, then we would clear mappings, rollback, + close tables, but the slave SQL thread would not stop and then may + assume the mapping is still available, the tables are still open... + So then we should clear mappings/rollback/close here only if this is a + 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); + thd->query_error= 1; + DBUG_RETURN(error); + } + + /* + This code would ideally be placed in do_update_pos() instead, but + since we have no access to table there, we do the setting of + last_event_start_time here instead. + */ + if (table && (table->s->primary_key == MAX_KEY) && + !ev->cache_stmt && + ev->get_flags(Rows_log_event::STMT_END_F) == Rows_log_event::RLE_NO_FLAGS) + { + /* + ------------ Temporary fix until WL#2975 is implemented --------- + + This event is not the last one (no STMT_END_F). If we stop now + (in case of terminate_slave_thread()), how will we restart? We + have to restart from Table_map_log_event, but as this table is + not transactional, the rows already inserted will still be + present, and idempotency is not guaranteed (no PK) so we risk + that repeating leads to double insert. So we desperately try to + continue, hope we'll eventually leave this buggy situation (by + executing the final Rows_log_event). If we are in a hopeless + wait (reached end of last relay log and nothing gets appended + there), we timeout after one minute, and notify DBA about the + problem. When WL#2975 is implemented, just remove the member + st_relay_log_info::last_event_start_time and all its occurences. + */ + const_cast<Relay_log_info*>(rli)->last_event_start_time= my_time(0); + } + + DBUG_RETURN(0); +} +#endif + +#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) + +/* + Check if there are more UNIQUE keys after the given key. +*/ +static int +last_uniq_key(TABLE *table, uint keyno) +{ + while (++keyno < table->s->keys) + if (table->key_info[keyno].flags & HA_NOSAME) + return 0; + return 1; +} + +/* + Compares table->record[0] and table->record[1] + + Returns TRUE if different. +*/ +static bool record_compare(TABLE *table) +{ + /* + Need to set the X bit and the filler bits in both records since + there are engines that do not set it correctly. + + In addition, since MyISAM checks that one hasn't tampered with the + record, it is necessary to restore the old bytes into the record + after doing the comparison. + + TODO[record format ndb]: Remove it once NDB returns correct + records. Check that the other engines also return correct records. + */ + + bool result= FALSE; + uchar saved_x[2], saved_filler[2]; + + if (table->s->null_bytes > 0) + { + for (int i = 0 ; i < 2 ; ++i) + { + saved_x[i]= table->record[i][0]; + saved_filler[i]= table->record[i][table->s->null_bytes - 1]; + table->record[i][0]|= 1U; + table->record[i][table->s->null_bytes - 1]|= + 256U - (1U << table->s->last_null_bit_pos); + } + } + + if (table->s->blob_fields + table->s->varchar_fields == 0) + { + result= cmp_record(table,record[1]); + goto record_compare_exit; + } + + /* Compare null bits */ + if (memcmp(table->null_flags, + table->null_flags+table->s->rec_buff_length, + table->s->null_bytes)) + { + result= TRUE; // Diff in NULL value + goto record_compare_exit; + } + + /* Compare updated fields */ + for (Field **ptr=table->field ; *ptr ; ptr++) + { + if ((*ptr)->cmp_binary_offset(table->s->rec_buff_length)) + { + result= TRUE; + goto record_compare_exit; + } + } + +record_compare_exit: + /* + Restore the saved bytes. + + TODO[record format ndb]: Remove this code once NDB returns the + correct record format. + */ + if (table->s->null_bytes > 0) + { + for (int i = 0 ; i < 2 ; ++i) + { + table->record[i][0]= saved_x[i]; + table->record[i][table->s->null_bytes - 1]= saved_filler[i]; + } + } + + return result; +} + +/* + Copy "extra" columns from record[1] to record[0]. + + Copy the extra fields that are not present on the master but are + present on the slave from record[1] to record[0]. This is used + after fetching a record that are to be updated, either inside + replace_record() or as part of executing an update_row(). + */ +static int +copy_extra_record_fields(TABLE *table, + size_t master_reclength, + my_ptrdiff_t master_fields) +{ + DBUG_ENTER("copy_extra_record_fields(table, master_reclen, master_fields)"); + 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 + master into record[0] (which are basically the default values). + */ + + if (table->s->fields < (uint) master_fields) + DBUG_RETURN(0); + + DBUG_ASSERT(master_reclength <= table->s->reclength); + if (master_reclength < table->s->reclength) + bmove_align(table->record[0] + master_reclength, + table->record[1] + master_reclength, + table->s->reclength - master_reclength); + + /* + Bit columns are special. We iterate over all the remaining + columns and copy the "extra" bits to the new record. This is + not a very good solution: it should be refactored on + opportunity. + + REFACTORING SUGGESTION (Matz). Introduce a member function + similar to move_field_offset() called copy_field_offset() to + copy field values and implement it for all Field subclasses. Use + this function to copy data from the found record to the record + that are going to be inserted. + + The copy_field_offset() function need to be a virtual function, + which in this case will prevent copying an entire range of + fields efficiently. + */ + { + Field **field_ptr= table->field + master_fields; + for ( ; *field_ptr ; ++field_ptr) + { + /* + Set the null bit according to the values in record[1] + */ + if ((*field_ptr)->maybe_null() && + (*field_ptr)->is_null_in_record(reinterpret_cast<uchar*>(table->record[1]))) + (*field_ptr)->set_null(); + else + (*field_ptr)->set_notnull(); + + /* + Do the extra work for special columns. + */ + switch ((*field_ptr)->real_type()) + { + default: + /* Nothing to do */ + break; + + case MYSQL_TYPE_BIT: + Field_bit *f= static_cast<Field_bit*>(*field_ptr); + 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; + } + } + } + DBUG_RETURN(0); // All OK +} + +/* + Replace the provided record in the database. + + SYNOPSIS + replace_record() + thd Thread context for writing the record. + table Table to which record should be written. + master_reclength + Offset to first column that is not present on the master, + alternatively the length of the record on the master + side. + + RETURN VALUE + Error code on failure, 0 on success. + + DESCRIPTION + Similar to how it is done in mysql_insert(), we first try to do + a ha_write_row() and of that fails due to duplicated keys (or + indices), we do an ha_update_row() or a ha_delete_row() instead. + */ +static int +replace_record(THD *thd, TABLE *table, + ulong const master_reclength, + uint const master_fields) +{ + DBUG_ENTER("replace_record"); + DBUG_ASSERT(table != NULL && thd != NULL); + + int error; + int keynum; + auto_afree_ptr<char> key(NULL); + +#ifndef DBUG_OFF + DBUG_DUMP("record[0]", table->record[0], table->s->reclength); + DBUG_PRINT_BITSET("debug", "write_set = %s", table->write_set); + DBUG_PRINT_BITSET("debug", "read_set = %s", table->read_set); +#endif + + while ((error= table->file->ha_write_row(table->record[0]))) + { + if (error == HA_ERR_LOCK_DEADLOCK || error == HA_ERR_LOCK_WAIT_TIMEOUT) + { + 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) + { + table->file->print_error(error, MYF(0)); + /* + We failed to retrieve the duplicate key + - either because the error was not "duplicate key" error + - or because the information which key is not available + */ + DBUG_RETURN(error); + } + + /* + We need to retrieve the old row into record[1] to be able to + either update or delete the offending record. We either: + + - use rnd_pos() with a row-id (available as dupp_row) to the + offending row, if that is possible (MyISAM and Blackhole), or else + + - use index_read_idx() with the key that is duplicated, to + retrieve the offending row. + */ + if (table->file->ha_table_flags() & HA_DUPLICATE_POS) + { + error= table->file->rnd_pos(table->record[1], table->file->dup_ref); + if (error) + { + table->file->print_error(error, MYF(0)); + DBUG_RETURN(error); + } + } + else + { + if (table->file->extra(HA_EXTRA_FLUSH_CACHE)) + { + DBUG_RETURN(my_errno); + } + + if (key.get() == NULL) + { + key.assign(static_cast<char*>(my_alloca(table->s->max_unique_length))); + if (key.get() == NULL) + DBUG_RETURN(ENOMEM); + } + + key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum, + 0); + error= table->file->index_read_idx_map(table->record[1], keynum, + (const uchar*)key.get(), + HA_WHOLE_KEY, + HA_READ_KEY_EXACT); + if (error) + { + table->file->print_error(error, MYF(0)); + DBUG_RETURN(error); + } + } + + /* + Now, table->record[1] should contain the offending row. That + will enable us to update it or, alternatively, delete it (so + that we can insert the new row afterwards). + + First we copy the columns into table->record[0] that are not + 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 + work on InnoDB if FOREIGN KEY checks are necessary. + + I (Matz) am not sure of the reason for the last_uniq_key() + check as, but I'm guessing that it's something along the + following lines. + + Suppose that we got the duplicate key to be a key that is not + the last unique key for the table and we perform an update: + then there might be another key for which the unique check will + fail, so we're better off just deleting the row and inserting + the correct row. + */ + if (last_uniq_key(table, keynum) && + !table->file->referenced_by_foreign_key()) + { + error=table->file->ha_update_row(table->record[1], + table->record[0]); + if (error && error != HA_ERR_RECORD_IS_THE_SAME) + table->file->print_error(error, MYF(0)); + else + error= 0; + DBUG_RETURN(error); + } + else + { + if ((error= table->file->ha_delete_row(table->record[1]))) + { + table->file->print_error(error, MYF(0)); + DBUG_RETURN(error); + } + /* Will retry ha_write_row() with the offending row removed. */ + } + } + + DBUG_RETURN(error); +} + +/** + 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. + + @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, uchar *key) +{ + DBUG_ENTER("find_and_fetch_row(TABLE *table, uchar *key, uchar *record)"); + DBUG_PRINT("enter", ("table: 0x%lx, key: 0x%lx record: 0x%lx", + (long) table, (long) key, (long) table->record[1])); + + DBUG_ASSERT(table->in_use != NULL); + + DBUG_DUMP("record[0]", table->record[0], table->s->reclength); + + if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) && + table->s->primary_key < MAX_KEY) + { + /* + Use a more efficient method to fetch the record given by + table->record[0] if the engine allows it. We first compute a + 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]); + 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); + } + + /* We need to retrieve all fields */ + /* TODO: Move this out from this function to main loop */ + table->use_all_columns(); + + if (table->s->keys > 0) + { + int error; + /* We have a key: search the table using the index */ + if (!table->file->inited && (error= table->file->ha_index_init(0, FALSE))) + 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]", table->record[0], table->s->reclength); + DBUG_DUMP("table->record[1]", table->record[1], table->s->reclength); +#endif + + /* + We need to set the null bytes to ensure that the filler bit are + all set when returning. There are storage engines that just set + the necessary bits on the bytes and don't set the filler bits + correctly. + */ + my_ptrdiff_t const pos= + table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0; + table->record[1][pos]= 0xFF; + if ((error= table->file->index_read_map(table->record[1], key, HA_WHOLE_KEY, + HA_READ_KEY_EXACT))) + { + table->file->print_error(error, MYF(0)); + table->file->ha_index_end(); + 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]", table->record[0], table->s->reclength); + DBUG_DUMP("table->record[1]", 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 + correct record (since there can be no duplicates); otherwise, we + have to compare the record with the one found to see if it is + the correct one. + + CAVEAT! This behaviour is essential for the replication of, + e.g., the mysql.proc table since the correct record *shall* be + found using the primary key *only*. There shall be no + comparison of non-PK columns to decide if the correct record is + found. I can see no scenario where it would be incorrect to + chose the row to change only using a PK or an UNNI. + */ + if (table->key_info->flags & HA_NOSAME) + { + table->file->ha_index_end(); + DBUG_RETURN(0); + } + + while (record_compare(table)) + { + int error; + + /* + We need to set the null bytes to ensure that the filler bit + are all set when returning. There are storage engines that + just set the necessary bits on the bytes and don't set the + filler bits correctly. + + TODO[record format ndb]: Remove this code once NDB returns the + correct record format. + */ + if (table->s->null_bytes > 0) + { + table->record[1][table->s->null_bytes - 1]|= + 256U - (1U << table->s->last_null_bit_pos); + } + + if ((error= table->file->index_next(table->record[1]))) + { + table->file->print_error(error, MYF(0)); + table->file->ha_index_end(); + DBUG_RETURN(error); + } + } + + /* + Have to restart the scan to be able to fetch the next row. + */ + table->file->ha_index_end(); + } + else + { + int restart_count= 0; // Number of times scanning has restarted from top + int error; + + /* We don't have a key: search the table using rnd_next() */ + if ((error= table->file->ha_rnd_init(1))) + return error; + + /* Continue until we find the right record or have made a full loop */ + do + { + error= table->file->rnd_next(table->record[1]); + + DBUG_DUMP("record[0]", table->record[0], table->s->reclength); + DBUG_DUMP("record[1]", table->record[1], table->s->reclength); + + switch (error) { + case 0: + case HA_ERR_RECORD_DELETED: + break; + + case HA_ERR_END_OF_FILE: + if (++restart_count < 2) + table->file->ha_rnd_init(1); + break; + + default: + table->file->print_error(error, MYF(0)); + DBUG_PRINT("info", ("Record not found")); + table->file->ha_rnd_end(); + DBUG_RETURN(error); + } + } + while (restart_count < 2 && record_compare(table)); + + /* + Have to restart the scan to be able to fetch the next row. + */ + DBUG_PRINT("info", ("Record %sfound", restart_count == 2 ? "not " : "")); + table->file->ha_rnd_end(); + + DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == 0); + DBUG_RETURN(error); + } + + DBUG_RETURN(0); +} + +/********************************************************** + Row handling primitives for Write_rows_log_event_old + **********************************************************/ + +int Write_rows_log_event_old::do_before_row_operations(TABLE *table) +{ + int error= 0; + + /* + We are using REPLACE semantics and not INSERT IGNORE semantics + when writing rows, that is: new rows replace old rows. We need to + inform the storage engine that it should use this behaviour. + */ + + /* Tell the storage engine that we are using REPLACE semantics. */ + thd->lex->duplicates= DUP_REPLACE; + + /* + Pretend we're executing a REPLACE command: this is needed for + InnoDB and NDB Cluster since they are not (properly) checking the + lex->duplicates flag. + */ + thd->lex->sql_command= SQLCOM_REPLACE; + /* + Do not raise the error flag in case of hitting to an unique attribute + */ + table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); + /* + NDB specific: update from ndb master wrapped as Write_rows + */ + /* + so that the event should be applied to replace slave's row + */ + table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE); + /* + NDB specific: if update from ndb master wrapped as Write_rows + does not find the row it's assumed idempotent binlog applying + is taking place; don't raise the error. + */ + table->file->extra(HA_EXTRA_IGNORE_NO_KEY); + /* + TODO: the cluster team (Tomas?) says that it's better if the engine knows + how many rows are going to be inserted, then it can allocate needed memory + from the start. + */ + table->file->ha_start_bulk_insert(0); + /* + We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill + any TIMESTAMP column with data from the row but instead will use + the event's current time. + As we replicate from TIMESTAMP to TIMESTAMP and slave has no extra + columns, we know that all TIMESTAMP columns on slave will receive explicit + data from the row, so TIMESTAMP_NO_AUTO_SET is ok. + When we allow a table without TIMESTAMP to be replicated to a table having + more columns including a TIMESTAMP column, or when we allow a TIMESTAMP + column to be replicated into a BIGINT column and the slave's table has a + TIMESTAMP column, then the slave's TIMESTAMP column will take its value + from set_time() which we called earlier (consistent with SBR). And then in + some cases we won't want TIMESTAMP_NO_AUTO_SET (will require some code to + analyze if explicit data is provided for slave's TIMESTAMP columns). + */ + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; + return error; +} + +int Write_rows_log_event_old::do_after_row_operations(TABLE *table, int error) +{ + int local_error= 0; + table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); + table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); + /* + reseting the extra with + table->file->extra(HA_EXTRA_NO_IGNORE_NO_KEY); + fires bug#27077 + todo: explain or fix + */ + if ((local_error= table->file->ha_end_bulk_insert())) + { + table->file->print_error(local_error, MYF(0)); + } + return error? error : local_error; +} + int -Write_rows_log_event_old::do_prepare_row(THD *thd, - RELAY_LOG_INFO const *rli, +Write_rows_log_event_old::do_prepare_row(THD *thd_arg, + Relay_log_info const *rli, TABLE *table, uchar const *row_start, uchar const **row_end) @@ -15,7 +973,7 @@ Write_rows_log_event_old::do_prepare_row(THD *thd, DBUG_ASSERT(row_start && row_end); int error; - error= unpack_row_old(const_cast<RELAY_LOG_INFO*>(rli), + error= unpack_row_old(const_cast<Relay_log_info*>(rli), table, m_width, table->record[0], row_start, &m_cols, row_end, &m_master_reclength, table->write_set, PRE_GA_WRITE_ROWS_EVENT); @@ -23,10 +981,69 @@ Write_rows_log_event_old::do_prepare_row(THD *thd, return error; } +int Write_rows_log_event_old::do_exec_row(TABLE *table) +{ + DBUG_ASSERT(table != NULL); + int error= replace_record(thd, table, m_master_reclength, m_width); + return error; +} + +/********************************************************** + Row handling primitives for Delete_rows_log_event_old + **********************************************************/ + +int Delete_rows_log_event_old::do_before_row_operations(TABLE *table) +{ + DBUG_ASSERT(m_memory == NULL); + + if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) && + table->s->primary_key < MAX_KEY) + { + /* + We don't need to allocate any memory for m_after_image and + m_key since they are not used. + */ + return 0; + } + + int error= 0; + + if (table->s->keys > 0) + { + m_memory= (uchar*) my_multi_malloc(MYF(MY_WME), + &m_after_image, + (uint) table->s->reclength, + &m_key, + (uint) table->key_info->key_length, + NullS); + } + else + { + m_after_image= (uchar*) my_malloc(table->s->reclength, MYF(MY_WME)); + m_memory= (uchar*)m_after_image; + m_key= NULL; + } + if (!m_memory) + return HA_ERR_OUT_OF_MEM; + + return error; +} + +int Delete_rows_log_event_old::do_after_row_operations(TABLE *table, int error) +{ + /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/ + table->file->ha_index_or_rnd_end(); + my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR)); // Free for multi_malloc + m_memory= NULL; + m_after_image= NULL; + m_key= NULL; + + return error; +} int -Delete_rows_log_event_old::do_prepare_row(THD *thd, - RELAY_LOG_INFO const *rli, +Delete_rows_log_event_old::do_prepare_row(THD *thd_arg, + Relay_log_info const *rli, TABLE *table, uchar const *row_start, uchar const **row_end) @@ -39,7 +1056,7 @@ Delete_rows_log_event_old::do_prepare_row(THD *thd, */ DBUG_ASSERT(table->s->fields >= m_width); - error= unpack_row_old(const_cast<RELAY_LOG_INFO*>(rli), + error= unpack_row_old(const_cast<Relay_log_info*>(rli), table, m_width, table->record[0], row_start, &m_cols, row_end, &m_master_reclength, table->read_set, PRE_GA_DELETE_ROWS_EVENT); @@ -57,9 +1074,70 @@ Delete_rows_log_event_old::do_prepare_row(THD *thd, return error; } +int Delete_rows_log_event_old::do_exec_row(TABLE *table) +{ + int error; + DBUG_ASSERT(table != NULL); + + if (!(error= ::find_and_fetch_row(table, m_key))) + { + /* + Now we should have the right row to delete. We are using + record[0] since it is guaranteed to point to a record with the + correct value. + */ + error= table->file->ha_delete_row(table->record[0]); + } + return error; +} + +/********************************************************** + Row handling primitives for Update_rows_log_event_old + **********************************************************/ + +int Update_rows_log_event_old::do_before_row_operations(TABLE *table) +{ + DBUG_ASSERT(m_memory == NULL); + + int error= 0; + + if (table->s->keys > 0) + { + m_memory= (uchar*) my_multi_malloc(MYF(MY_WME), + &m_after_image, + (uint) table->s->reclength, + &m_key, + (uint) table->key_info->key_length, + NullS); + } + else + { + m_after_image= (uchar*) my_malloc(table->s->reclength, MYF(MY_WME)); + m_memory= m_after_image; + m_key= NULL; + } + if (!m_memory) + return HA_ERR_OUT_OF_MEM; + + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; + + return error; +} + +int Update_rows_log_event_old::do_after_row_operations(TABLE *table, int error) +{ + /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/ + table->file->ha_index_or_rnd_end(); + my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR)); + m_memory= NULL; + m_after_image= NULL; + m_key= NULL; + + return error; +} -int Update_rows_log_event_old::do_prepare_row(THD *thd, - RELAY_LOG_INFO const *rli, +int Update_rows_log_event_old::do_prepare_row(THD *thd_arg, + Relay_log_info const *rli, TABLE *table, uchar const *row_start, uchar const **row_end) @@ -73,13 +1151,13 @@ int Update_rows_log_event_old::do_prepare_row(THD *thd, DBUG_ASSERT(table->s->fields >= m_width); /* record[0] is the before image for the update */ - error= unpack_row_old(const_cast<RELAY_LOG_INFO*>(rli), + error= unpack_row_old(const_cast<Relay_log_info*>(rli), table, m_width, table->record[0], row_start, &m_cols, row_end, &m_master_reclength, table->read_set, PRE_GA_UPDATE_ROWS_EVENT); row_start = *row_end; /* m_after_image is the after image for the update */ - error= unpack_row_old(const_cast<RELAY_LOG_INFO*>(rli), + error= unpack_row_old(const_cast<Relay_log_info*>(rli), table, m_width, m_after_image, row_start, &m_cols, row_end, &m_master_reclength, table->write_set, PRE_GA_UPDATE_ROWS_EVENT); @@ -101,4 +1179,41 @@ int Update_rows_log_event_old::do_prepare_row(THD *thd, return error; } +int Update_rows_log_event_old::do_exec_row(TABLE *table) +{ + DBUG_ASSERT(table != NULL); + + int error= ::find_and_fetch_row(table, m_key); + if (error) + return error; + + /* + We have to ensure that the new record (i.e., the after image) is + in record[0] and the old record (i.e., the before image) is in + record[1]. This since some storage engines require this (for + example, the partition engine). + + Since find_and_fetch_row() puts the fetched record (i.e., the old + record) in record[1], we can keep it there. We put the new record + (i.e., the after image) into record[0], and copy the fields that + are on the slave (i.e., in record[1]) into record[0], effectively + overwriting the default values that where put there by the + unpack_row() function. + */ + bmove_align(table->record[0], m_after_image, table->s->reclength); + copy_extra_record_fields(table, m_master_reclength, m_width); + + /* + Now we have the right row to update. The old row (the one we're + looking for) is in record[1] and the new row has is in record[0]. + We also have copied the original values already in the slave's + database into the after image delivered from the master. + */ + error= table->file->ha_update_row(table->record[1], table->record[0]); + if (error == HA_ERR_RECORD_IS_THE_SAME) + error= 0; + + return error; +} + #endif diff --git a/sql/log_event_old.h b/sql/log_event_old.h index 7c2932360f5..81e55097905 100644 --- a/sql/log_event_old.h +++ b/sql/log_event_old.h @@ -20,9 +20,90 @@ Need to include this file at the proper position of log_event.h */ + +class Old_rows_log_event +{ + public: + + virtual ~Old_rows_log_event() {} + + protected: + +#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) + + int do_apply_event(Rows_log_event*,const Relay_log_info*); + + /* + Primitive to prepare for a sequence of row executions. + + DESCRIPTION + + Before doing a sequence of do_prepare_row() and do_exec_row() + calls, this member function should be called to prepare for the + entire sequence. Typically, this member function will allocate + space for any buffers that are needed for the two member + functions mentioned above. + + RETURN VALUE + + The member function will return 0 if all went OK, or a non-zero + error code otherwise. + */ + virtual int do_before_row_operations(TABLE *table) = 0; + + /* + Primitive to clean up after a sequence of row executions. + + DESCRIPTION + + After doing a sequence of do_prepare_row() and do_exec_row(), + this member function should be called to clean up and release + any allocated buffers. + */ + virtual int do_after_row_operations(TABLE *table, int error) = 0; + + /* + Primitive to prepare for handling one row in a row-level event. + + DESCRIPTION -class Write_rows_log_event_old : public Write_rows_log_event + The member function prepares for execution of operations needed for one + row in a row-level event by reading up data from the buffer containing + the row. No specific interpretation of the data is normally done here, + since SQL thread specific data is not available: that data is made + available for the do_exec function. + + A pointer to the start of the next row, or NULL if the preparation + failed. Currently, preparation cannot fail, but don't rely on this + behavior. + + RETURN VALUE + Error code, if something went wrong, 0 otherwise. + */ + virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*, + uchar const *row_start, + uchar const **row_end) = 0; + + /* + Primitive to do the actual execution necessary for a row. + + DESCRIPTION + The member function will do the actual execution needed to handle a row. + + RETURN VALUE + 0 if execution succeeded, 1 if execution failed. + + */ + virtual int do_exec_row(TABLE *table) = 0; + +#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */ +}; + + +class Write_rows_log_event_old + : public Write_rows_log_event, public Old_rows_log_event { + public: enum { @@ -49,14 +130,26 @@ private: virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; } #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_prepare_row(THD*, RELAY_LOG_INFO const*, TABLE*, + // use old definition of do_apply_event() + virtual int do_apply_event(const Relay_log_info *rli) + { return Old_rows_log_event::do_apply_event(this,rli); } + + // primitives for old version of do_apply_event() + virtual int do_before_row_operations(TABLE *table); + virtual int do_after_row_operations(TABLE *table, int error); + virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*, uchar const *row_start, uchar const **row_end); + virtual int do_exec_row(TABLE *table); + #endif }; -class Update_rows_log_event_old : public Update_rows_log_event +class Update_rows_log_event_old + : public Update_rows_log_event, public Old_rows_log_event { + uchar *m_after_image, *m_memory; + public: enum { @@ -67,14 +160,16 @@ public: #if !defined(MYSQL_CLIENT) Update_rows_log_event_old(THD *thd, TABLE *table, ulong table_id, MY_BITMAP const *cols, bool is_transactional) - : Update_rows_log_event(thd, table, table_id, cols, is_transactional) + : Update_rows_log_event(thd, table, table_id, cols, is_transactional), + m_after_image(NULL), m_memory(NULL) { } #endif #if defined(HAVE_REPLICATION) Update_rows_log_event_old(const char *buf, uint event_len, const Format_description_log_event *descr) - : Update_rows_log_event(buf, event_len, descr) + : Update_rows_log_event(buf, event_len, descr), + m_after_image(NULL), m_memory(NULL) { } #endif @@ -83,14 +178,25 @@ private: virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; } #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_prepare_row(THD*, RELAY_LOG_INFO const*, TABLE*, + // use old definition of do_apply_event() + virtual int do_apply_event(const Relay_log_info *rli) + { return Old_rows_log_event::do_apply_event(this,rli); } + + // primitives for old version of do_apply_event() + virtual int do_before_row_operations(TABLE *table); + virtual int do_after_row_operations(TABLE *table, int error); + virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*, uchar const *row_start, uchar const **row_end); + virtual int do_exec_row(TABLE *table); #endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */ }; -class Delete_rows_log_event_old : public Delete_rows_log_event +class Delete_rows_log_event_old + : public Delete_rows_log_event, public Old_rows_log_event { + uchar *m_after_image, *m_memory; + public: enum { @@ -101,14 +207,16 @@ public: #if !defined(MYSQL_CLIENT) Delete_rows_log_event_old(THD *thd, TABLE *table, ulong table_id, MY_BITMAP const *cols, bool is_transactional) - : Delete_rows_log_event(thd, table, table_id, cols, is_transactional) + : Delete_rows_log_event(thd, table, table_id, cols, is_transactional), + m_after_image(NULL), m_memory(NULL) { } #endif #if defined(HAVE_REPLICATION) Delete_rows_log_event_old(const char *buf, uint event_len, const Format_description_log_event *descr) - : Delete_rows_log_event(buf, event_len, descr) + : Delete_rows_log_event(buf, event_len, descr), + m_after_image(NULL), m_memory(NULL) { } #endif @@ -117,8 +225,16 @@ private: virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; } #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_prepare_row(THD*, RELAY_LOG_INFO const*, TABLE*, + // use old definition of do_apply_event() + virtual int do_apply_event(const Relay_log_info *rli) + { return Old_rows_log_event::do_apply_event(this,rli); } + + // primitives for old version of do_apply_event() + virtual int do_before_row_operations(TABLE *table); + virtual int do_after_row_operations(TABLE *table, int error); + virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*, uchar const *row_start, uchar const **row_end); + virtual int do_exec_row(TABLE *table); #endif }; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index b52bdf90a69..1400fe3e1bb 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -167,7 +167,8 @@ public: void restore_env(THD *thd, Object_creation_ctx *backup_ctx); protected: - virtual Object_creation_ctx *create_backup_ctx(THD *thd) = 0; + Object_creation_ctx() {} + virtual Object_creation_ctx *create_backup_ctx(THD *thd) const = 0; virtual void change_env(THD *thd) const = 0; @@ -203,7 +204,7 @@ protected: CHARSET_INFO *connection_cl); protected: - virtual Object_creation_ctx *create_backup_ctx(THD *thd); + virtual Object_creation_ctx *create_backup_ctx(THD *thd) const; virtual void change_env(THD *thd) const; @@ -531,9 +532,9 @@ void debug_sync_point(const char* lock_name, uint lock_timeout); #define SHOW_LOG_STATUS_FREE "FREE" #define SHOW_LOG_STATUS_INUSE "IN USE" -struct st_table_list; +struct TABLE_LIST; class String; -void view_store_options(THD *thd, st_table_list *table, String *buff); +void view_store_options(THD *thd, TABLE_LIST *table, String *buff); /* Options to add_table_to_list() */ #define TL_OPTION_UPDATING 1 @@ -574,6 +575,13 @@ enum enum_parsing_place struct st_table; class THD; +enum enum_check_fields +{ + CHECK_FIELD_IGNORE, + CHECK_FIELD_WARN, + CHECK_FIELD_ERROR_FOR_NULL +}; + /* Struct to handle simple linked lists */ typedef struct st_sql_list { @@ -712,6 +720,26 @@ bool end_active_trans(THD *thd); int end_trans(THD *thd, enum enum_mysql_completiontype completion); Item *negate_expression(THD *thd, Item *expr); + +/* log.cc */ +int vprint_msg_to_log(enum loglevel level, const char *format, va_list args); +void sql_print_error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); +void sql_print_warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); +void sql_print_information(const char *format, ...) + ATTRIBUTE_FORMAT(printf, 1, 2); +typedef void (*sql_print_message_func)(const char *format, ...) + ATTRIBUTE_FORMAT(printf, 1, 2); +extern sql_print_message_func sql_print_message_handlers[]; + +int error_log_print(enum loglevel level, const char *format, + va_list args); + +bool slow_log_print(THD *thd, const char *query, uint query_length, + ulonglong current_utime); + +bool general_log_print(THD *thd, enum enum_server_command command, + const char *format,...); + #include "sql_class.h" #include "sql_acl.h" #include "tztime.h" @@ -901,10 +929,7 @@ void mysql_client_binlog_statement(THD *thd); bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, my_bool drop_temporary); int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, - bool drop_temporary, bool drop_view, bool log_query); -int mysql_rm_table_part2_with_lock(THD *thd, TABLE_LIST *tables, - bool if_exists, bool drop_temporary, - bool log_query); + bool drop_temporary, bool drop_view, bool log_query); bool quick_rm_table(handlerton *base,const char *db, const char *table_name, uint flags); void close_cached_table(THD *thd, TABLE *table); @@ -912,14 +937,22 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent); bool do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name, char *new_table_alias, bool skip_error); + bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch); +bool mysql_opt_change_db(THD *thd, + const LEX_STRING *new_db_name, + LEX_STRING *saved_db_name, + bool force_switch, + bool *cur_db_changed); + void mysql_parse(THD *thd, const char *inBuf, uint length, const char ** semicolon); bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length); bool is_update_query(enum enum_sql_command command); +bool is_log_table_write_query(enum enum_sql_command command); bool alloc_query(THD *thd, const char *packet, uint packet_length); void mysql_init_select(LEX *lex); void mysql_reset_thd_for_next_command(THD *thd); @@ -1099,7 +1132,8 @@ TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key, uint key_length, uint db_flags, int *error); void release_table_share(TABLE_SHARE *share, enum release_type type); TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name); -TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update); +TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update, + uint lock_flags); TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem, bool *refresh, uint flags); bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in); @@ -1175,7 +1209,11 @@ bool mysqld_show_open_tables(THD *thd,const char *wild); bool mysqld_show_logs(THD *thd); void append_identifier(THD *thd, String *packet, const char *name, uint length); +#endif /* MYSQL_SERVER */ +#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS int get_quote_char_for_identifier(THD *thd, const char *name, uint length); +#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */ +#ifdef MYSQL_SERVER void mysqld_list_fields(THD *thd,TABLE_LIST *table, const char *wild); int mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd); bool mysqld_show_create(THD *thd, TABLE_LIST *table_list); @@ -1203,10 +1241,12 @@ void reset_status_vars(); /* information schema */ extern LEX_STRING INFORMATION_SCHEMA_NAME; +/* log tables */ +extern LEX_STRING MYSQL_SCHEMA_NAME; +extern LEX_STRING GENERAL_LOG_NAME; +extern LEX_STRING SLOW_LOG_NAME; + extern const LEX_STRING partition_keywords[]; -LEX_STRING *make_lex_string(THD *thd, LEX_STRING *lex_str, - const char* str, uint length, - bool allocate_lex_string); ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name); ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx); int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, @@ -1245,7 +1285,6 @@ 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 @@ -1367,7 +1406,7 @@ bool close_thread_table(THD *thd, TABLE **table_ptr); void close_temporary_tables(THD *thd); void close_tables_for_reopen(THD *thd, TABLE_LIST **tables); TABLE_LIST *find_table_in_list(TABLE_LIST *table, - st_table_list *TABLE_LIST::*link, + 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, @@ -1532,6 +1571,10 @@ bool open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, void close_system_tables(THD *thd, Open_tables_state *backup); TABLE *open_system_table_for_update(THD *thd, TABLE_LIST *one_table); +TABLE *open_performance_schema_table(THD *thd, TABLE_LIST *one_table, + Open_tables_state *backup); +void close_performance_schema_table(THD *thd, Open_tables_state *backup); + bool close_cached_tables(THD *thd, bool wait_for_refresh, TABLE_LIST *tables, bool have_lock = FALSE); bool close_cached_connection_tables(THD *thd, bool wait_for_refresh, LEX_STRING *connect_string, @@ -1606,31 +1649,12 @@ bool key_cmp_if_same(TABLE *form,const uchar *key,uint index,uint key_length); void key_unpack(String *to,TABLE *form,uint index); bool is_key_used(TABLE *table, uint idx, const MY_BITMAP *fields); int key_cmp(KEY_PART_INFO *key_part, const uchar *key, uint key_length); -int key_rec_cmp(void *key_info, uchar *a, uchar *b); +extern "C" int key_rec_cmp(void *key_info, uchar *a, uchar *b); bool init_errmessage(void); #endif /* MYSQL_SERVER */ void sql_perror(const char *message); - -int vprint_msg_to_log(enum loglevel level, const char *format, va_list args); -void sql_print_error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); -void sql_print_warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); -void sql_print_information(const char *format, ...) - ATTRIBUTE_FORMAT(printf, 1, 2); -typedef void (*sql_print_message_func)(const char *format, ...) - ATTRIBUTE_FORMAT(printf, 1, 2); -extern sql_print_message_func sql_print_message_handlers[]; - -int error_log_print(enum loglevel level, const char *format, - va_list args); - -bool slow_log_print(THD *thd, const char *query, uint query_length, - time_t query_start_arg); - -bool general_log_print(THD *thd, enum enum_server_command command, - const char *format,...); - bool fn_format_relative_to_data_home(char * to, const char *name, const char *dir, const char *extension); #ifdef MYSQL_SERVER @@ -1684,9 +1708,14 @@ extern int creating_table; // How many mysql_create_table() are running */ extern time_t server_start_time; +#endif /* MYSQL_SERVER */ +#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS extern uint mysql_data_home_len; extern char *mysql_data_home,server_version[SERVER_VERSION_LENGTH], - mysql_real_data_home[], *opt_mysql_tmpdir, mysql_charsets_dir[], + mysql_real_data_home[]; +#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */ +#ifdef MYSQL_SERVER +extern char *opt_mysql_tmpdir, mysql_charsets_dir[], def_ft_boolean_syntax[sizeof(ft_boolean_syntax)]; #define mysql_tmpdir (my_tmpdir(&mysql_tmpdir_list)) extern MY_TMPDIR mysql_tmpdir_list; @@ -1703,8 +1732,13 @@ extern Gt_creator gt_creator; extern Lt_creator lt_creator; extern Ge_creator ge_creator; extern Le_creator le_creator; -extern char language[FN_REFLEN], reg_ext[FN_EXTLEN]; +extern char language[FN_REFLEN]; +#endif /* MYSQL_SERVER */ +#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS +extern char reg_ext[FN_EXTLEN]; extern uint reg_ext_length; +#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */ +#ifdef MYSQL_SERVER 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; @@ -1733,17 +1767,32 @@ extern ulong max_binlog_size, max_relay_log_size; extern ulong opt_binlog_rows_event_max_size; extern ulong rpl_recovery_rank, thread_cache_size, thread_pool_size; extern ulong back_log; -extern ulong specialflag, current_pid; +#endif /* MYSQL_SERVER */ +#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS +extern ulong specialflag; +#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */ +#ifdef MYSQL_SERVER +extern ulong current_pid; extern ulong expire_logs_days, sync_binlog_period, sync_binlog_counter; extern ulong opt_tc_log_size, tc_log_max_pages_used, tc_log_page_size; extern ulong tc_log_page_waits; extern my_bool relay_log_purge, opt_innodb_safe_binlog, opt_innodb; extern uint test_flags,select_errors,ha_open_options; extern uint protocol_version, mysqld_port, dropping_tables; -extern uint delay_key_write_options, lower_case_table_names; +extern uint delay_key_write_options; +#endif /* MYSQL_SERVER */ +#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS +extern uint lower_case_table_names; +#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */ +#ifdef MYSQL_SERVER extern bool opt_endinfo, using_udf_functions; extern my_bool locked_in_memory; -extern bool opt_using_transactions, mysqld_embedded; +extern bool opt_using_transactions; +#endif /* MYSQL_SERVER */ +#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS +extern bool mysqld_embedded; +#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */ +#ifdef MYSQL_SERVER extern bool using_update_log, opt_large_files, server_id_supplied; extern bool opt_update_log, opt_bin_log, opt_error_log; extern my_bool opt_log, opt_slow_log; @@ -1760,15 +1809,19 @@ 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 opt_log_slow_admin_statements, opt_log_slow_slave_statements; extern my_bool sp_automatic_privileges, opt_noacl; extern my_bool opt_old_style_user_limits, trust_function_creators; extern uint opt_crash_binlog_innodb; extern char *shared_memory_base_name, *mysqld_unix_port; extern my_bool opt_enable_shared_memory; extern char *default_tz_name; +#endif /* MYSQL_SERVER */ +#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS extern my_bool opt_large_pages; extern uint opt_large_page_size; +#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */ +#ifdef MYSQL_SERVER extern char *opt_logname, *opt_slow_logname; extern const char *log_output_str; @@ -1807,7 +1860,11 @@ extern MY_BITMAP temp_pool; extern String my_empty_string; extern const String my_null_string; extern SHOW_VAR status_vars[]; +#endif /* MYSQL_SERVER */ +#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS extern struct system_variables global_system_variables; +#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */ +#ifdef MYSQL_SERVER extern struct system_variables max_system_variables; extern struct system_status_var global_status_var; extern struct my_rnd_struct sql_rand; @@ -1855,11 +1912,13 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, #define MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN 0x0004 #define MYSQL_OPEN_TEMPORARY_ONLY 0x0008 #define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0010 +#define MYSQL_LOCK_PERF_SCHEMA 0x0020 void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count); -void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table); +void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table, + bool always_unlock); void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock); void mysql_lock_downgrade_write(THD *thd, TABLE *table, thr_lock_type new_lock_type); @@ -1885,6 +1944,11 @@ bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list); bool lock_table_names(THD *thd, TABLE_LIST *table_list); void unlock_table_names(THD *thd, TABLE_LIST *table_list, TABLE_LIST *last_table); +bool lock_table_names_exclusively(THD *thd, TABLE_LIST *table_list); +bool is_table_name_exclusively_locked_by_this_thread(THD *thd, + TABLE_LIST *table_list); +bool is_table_name_exclusively_locked_by_this_thread(THD *thd, uchar *key, + int key_length); /* old unireg functions */ @@ -2005,10 +2069,14 @@ int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr); char *fn_rext(char *name); /* Conversion functions */ +#endif /* MYSQL_SERVER */ +#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS uint strconvert(CHARSET_INFO *from_cs, const char *from, CHARSET_INFO *to_cs, char *to, uint to_length, uint *errors); uint filename_to_tablename(const char *from, char *to, uint to_length); uint tablename_to_filename(const char *from, char *to, uint to_length); +#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */ +#ifdef MYSQL_SERVER uint build_table_filename(char *buff, size_t bufflen, const char *db, const char *table, const char *ext, uint flags); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 4b802c83eb6..6d172aa1330 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -342,6 +342,7 @@ static char *default_collation_name; static char *default_storage_engine_str; static char compiled_default_collation_name[]= MYSQL_DEFAULT_COLLATION_NAME; static I_List<THD> thread_cache; +static double long_query_time; static pthread_cond_t COND_thread_cache, COND_flush_thread_cache; @@ -410,6 +411,7 @@ 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 opt_log_slow_slave_statements= 0; my_bool lower_case_file_system= 0; my_bool opt_large_pages= 0; my_bool opt_myisam_use_mmap= 0; @@ -504,7 +506,6 @@ 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"; -const char *maria_stats_method_str="nulls_unequal"; /* name of reference on left espression in rewritten IN subquery */ const char *in_left_expr_name= "<left expr>"; @@ -548,7 +549,7 @@ MY_LOCALE *my_default_lc_time_names; 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, have_maria_db; +SHOW_COMP_OPTION have_crypt, have_compress; /* Thread specific variables */ @@ -699,7 +700,7 @@ struct st_VioSSLFd *ssl_acceptor_fd; pthread_handler_t signal_hand(void *arg); static void mysql_init_variables(void); static void get_options(int *argc,char **argv); -static my_bool get_one_option(int, const struct my_option *, char *); +extern "C" my_bool mysqld_get_one_option(int, const struct my_option *, char *); static void set_server_version(void); static int init_thread_environment(); static char *get_relative_path(const char *path); @@ -752,14 +753,16 @@ static void close_connections(void) (void) pthread_mutex_lock(&LOCK_manager); if (manager_thread_in_use) { - DBUG_PRINT("quit",("killing manager thread: 0x%lx",manager_thread)); + DBUG_PRINT("quit", ("killing manager thread: 0x%lx", + (ulong)manager_thread)); (void) pthread_cond_signal(&COND_manager); } (void) pthread_mutex_unlock(&LOCK_manager); /* kill connection thread */ #if !defined(__WIN__) && !defined(__NETWARE__) - DBUG_PRINT("quit",("waiting for select thread: 0x%lx",select_thread)); + DBUG_PRINT("quit", ("waiting for select thread: 0x%lx", + (ulong) select_thread)); (void) pthread_mutex_lock(&LOCK_thread_count); while (select_thread_in_use) @@ -1147,11 +1150,12 @@ void unireg_end(void) extern "C" void unireg_abort(int exit_code) { DBUG_ENTER("unireg_abort"); + if (exit_code) sql_print_error("Aborting\n"); else if (opt_help) usage(); - clean_up(exit_code || !opt_bootstrap); /* purecov: inspected */ + clean_up(!opt_help && (exit_code || !opt_bootstrap)); /* purecov: inspected */ DBUG_PRINT("quit",("done with cleanup in unireg_abort")); wait_for_signal_thread_to_end(); clean_up_mutexes(); @@ -1254,12 +1258,12 @@ void clean_up(bool print_message) my_regex_end(); #endif - if (print_message && errmesg) - sql_print_information(ER(ER_SHUTDOWN_COMPLETE),my_progname); #if !defined(EMBEDDED_LIBRARY) if (!opt_bootstrap) (void) my_delete(pidfile_name,MYF(0)); // This may not always exist #endif + if (print_message && errmesg && server_start_time) + sql_print_information(ER(ER_SHUTDOWN_COMPLETE),my_progname); thread_scheduler.end(); finish_client_errs(); my_free((uchar*) my_error_unregister(ER_ERROR_FIRST, ER_ERROR_LAST), @@ -1299,7 +1303,7 @@ static void wait_for_signal_thread_to_end() */ for (i= 0 ; i < 100 && signal_thread_in_use; i++) { - if (pthread_kill(signal_thread, MYSQL_KILL_SIGNAL)) + if (pthread_kill(signal_thread, MYSQL_KILL_SIGNAL) != ESRCH) break; my_sleep(100); // Give it time to die } @@ -1811,7 +1815,7 @@ static bool cache_thread() this thread for handling of new THD object/connection. */ thd->mysys_var->abort= 0; - thd->thr_create_time= time(NULL); + thd->thr_create_utime= my_micro_time(); threads.append(thd); return(1); } @@ -2188,7 +2192,7 @@ extern "C" sig_handler handle_segfault(int sig) segfaulted = 1; - curr_time= time(NULL); + curr_time= my_time(0); localtime_r(&curr_time, &tm); fprintf(stderr,"\ @@ -2203,7 +2207,7 @@ or misconfigured. This error can also be caused by malfunctioning hardware.\n", We will try our best to scrape up some info that will hopefully help diagnose\n\ the problem, but since we have already crashed, something is definitely wrong\n\ and this may fail.\n\n"); - fprintf(stderr, "key_buffer_size=%lu\n", + fprintf(stderr, "key_buffer_size=%lu\n", (ulong) dflt_key_cache->key_cache_mem_size); #ifdef WITH_MARIA_STORAGE_ENGINE fprintf(stderr, "page_buffer_size=%lu\n", @@ -2372,7 +2376,7 @@ static void init_signals(void) sigaddset(&set,SIGTSTP); #endif if (thd_lib_detected != THD_LIB_LT) - sigaddset(&set,THR_SERVER_ALARM); + sigaddset(&set,THR_SERVER_ALARM); if (test_flags & TEST_SIGINT) { // May be SIGINT @@ -2568,7 +2572,9 @@ static void check_data_home(const char *path) /* ARGSUSED */ -static int my_message_sql(uint error, const char *str, myf MyFlags) +extern "C" int my_message_sql(uint error, const char *str, myf MyFlags); + +int my_message_sql(uint error, const char *str, myf MyFlags) { THD *thd; DBUG_ENTER("my_message_sql"); @@ -2631,13 +2637,16 @@ static int my_message_sql(uint error, const char *str, myf MyFlags) #ifndef EMBEDDED_LIBRARY -static void *my_str_malloc_mysqld(size_t size) +extern "C" void *my_str_malloc_mysqld(size_t size); +extern "C" void my_str_free_mysqld(void *ptr); + +void *my_str_malloc_mysqld(size_t size) { return my_malloc(size, MYF(MY_FAE)); } -static void my_str_free_mysqld(void *ptr) +void my_str_free_mysqld(void *ptr) { my_free((uchar*)ptr, MYF(MY_FAE)); } @@ -2738,7 +2747,7 @@ static int init_common_variables(const char *conf_file_name, int argc, tzset(); // Set tzname max_system_variables.pseudo_thread_id= (ulong)~0; - server_start_time= time((time_t*) 0); + server_start_time= my_time(0); rpl_filter= new Rpl_filter; binlog_filter= new Rpl_filter; if (!rpl_filter || !binlog_filter) @@ -2973,7 +2982,7 @@ 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-slow-queries option, log tables are used. " - "To enable logging to files use the --log-output option."); + "To enable logging to files use the --log-output=file option."); s= opt_logname ? opt_logname : make_default_log_name(buff, ".log"); sys_var_general_log_path.value= my_strdup(s, MYF(0)); @@ -3452,7 +3461,7 @@ server."); my_getopt_skip_unknown= 0; if ((ho_error= handle_options(&defaults_argc, &tmp_argv, no_opts, - get_one_option))) + mysqld_get_one_option))) unireg_abort(ho_error); if (defaults_argc) @@ -3576,7 +3585,7 @@ server."); #ifdef HAVE_REPLICATION if (opt_bin_log && expire_logs_days) { - time_t purge_time= time(0) - expire_logs_days*24*60*60; + time_t purge_time= server_start_time - expire_logs_days*24*60*60; if (purge_time >= 0) mysql_bin_log.purge_logs_before_date(purge_time); } @@ -3933,7 +3942,6 @@ we force server id to 2, but this MySQL server will not act as a slave."); */ if (init_slave() && !active_mi) { - end_thr_alarm(1); // Don't allow alarms unireg_abort(1); } @@ -3941,16 +3949,12 @@ we force server id to 2, but this MySQL server will not act as a slave."); { select_thread_in_use= 0; // Allow 'kill' to work bootstrap(stdin); - end_thr_alarm(1); // Don't allow alarms unireg_abort(bootstrap_error ? 1 : 0); } if (opt_init_file) { if (read_init_file(opt_init_file)) - { - end_thr_alarm(1); // Don't allow alarms unireg_abort(1); - } } execute_ddl_log_recovery(); @@ -4322,7 +4326,7 @@ void create_thread_to_handle_connection(THD *thd) thread_created++; threads.append(thd); DBUG_PRINT("info",(("creating thread %lu"), thd->thread_id)); - thd->connect_time = time(NULL); + thd->connect_utime= thd->start_utime= my_micro_time(); if ((error=pthread_create(&thd->real_id,&connection_attrib, handle_one_connection, (void*) thd))) @@ -4385,6 +4389,11 @@ static void create_new_thread(THD *thd) DBUG_VOID_RETURN; } pthread_mutex_lock(&LOCK_thread_count); + /* + The initialization of thread_id is done in create_embedded_thd() for + the embedded library. + TODO: refactor this to avoid code duplication there + */ thd->thread_id= thd->variables.pseudo_thread_id= thread_id++; /* Start a new thread to handle connection */ @@ -5049,10 +5058,6 @@ enum options_mysqld OPT_MYISAM_USE_MMAP, OPT_MYISAM_REPAIR_THREADS, OPT_MYISAM_STATS_METHOD, - OPT_MARIA_BLOCK_SIZE, - OPT_MARIA_MAX_SORT_FILE_SIZE, OPT_MARIA_SORT_BUFFER_SIZE, - OPT_MARIA_USE_MMAP, OPT_MARIA_REPAIR_THREADS, - OPT_MARIA_STATS_METHOD, OPT_PAGECACHE_BUFFER_SIZE, OPT_PAGECACHE_DIVISION_LIMIT, OPT_PAGECACHE_AGE_THRESHOLD, @@ -5069,7 +5074,7 @@ enum options_mysqld OPT_SORT_BUFFER, OPT_TABLE_OPEN_CACHE, OPT_TABLE_DEF_CACHE, OPT_THREAD_CONCURRENCY, OPT_THREAD_CACHE_SIZE, OPT_TMP_TABLE_SIZE, OPT_THREAD_STACK, - OPT_WAIT_TIMEOUT, + OPT_WAIT_TIMEOUT, OPT_ERROR_LOG_FILE, OPT_DEFAULT_WEEK_FORMAT, OPT_RANGE_ALLOC_BLOCK_SIZE, OPT_ALLOW_SUSPICIOUS_UDFS, @@ -5113,11 +5118,14 @@ enum options_mysqld OPT_PLUGIN_DIR, OPT_LOG_OUTPUT, OPT_PORT_OPEN_TIMEOUT, + OPT_KEEP_FILES_ON_CREATE, OPT_GENERAL_LOG, OPT_SLOW_LOG, OPT_THREAD_HANDLING, OPT_INNODB_ROLLBACK_ON_TIMEOUT, OPT_SECURE_FILE_PRIV, + OPT_MIN_EXAMINED_ROW_LIMIT, + OPT_LOG_SLOW_SLAVE_STATEMENTS, OPT_OLD_MODE }; @@ -5419,8 +5427,13 @@ Disable with --skip-large-pages.", (uchar**) &opt_log_slow_admin_statements, (uchar**) &opt_log_slow_admin_statements, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"log-slow-slave-statements", OPT_LOG_SLOW_SLAVE_STATEMENTS, + "Log slow statements executed by slave thread to the slow log if it is open.", + (uchar**) &opt_log_slow_slave_statements, + (uchar**) &opt_log_slow_slave_statements, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"log-slow-queries", OPT_SLOW_QUERY_LOG, - "Log slow queries to this log file. Defaults logging to hostname-slow.log file. Must be enabled to activate other slow log options.", + "Log slow queries to a table or log file. Defaults logging to table mysql.slow_log or hostname-slow.log if --log-output=file is used. Must be enabled to activate other slow log options.", (uchar**) &opt_slow_logname, (uchar**) &opt_slow_logname, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"log-tc", OPT_LOG_TC, @@ -5983,6 +5996,11 @@ log and this option does nothing anymore.", (uchar**) &max_system_variables.join_buff_size, 0, GET_ULONG, REQUIRED_ARG, 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE, 0}, + {"keep_files_on_create", OPT_KEEP_FILES_ON_CREATE, + "Don't overwrite stale .MYD and .MYI even if no directory is specified.", + (uchar**) &global_system_variables.keep_files_on_create, + (uchar**) &max_system_variables.keep_files_on_create, + 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"key_buffer_size", OPT_KEY_BUFFER_SIZE, "The size of the buffer used for index blocks for MyISAM tables. Increase this to get better index handling (for all reads and multiple writes) to as much as you can afford; 64M on a 256M machine that mainly runs MySQL is quite common.", (uchar**) &dflt_key_cache_var.param_buff_size, @@ -6009,10 +6027,10 @@ log and this option does nothing anymore.", 0, (GET_ULONG | GET_ASK_ADDR) , REQUIRED_ARG, 100, 1, 100, 0, 1, 0}, {"long_query_time", OPT_LONG_QUERY_TIME, - "Log all queries that have taken more than long_query_time seconds to execute to file.", - (uchar**) &global_system_variables.long_query_time, - (uchar**) &max_system_variables.long_query_time, 0, GET_ULONG, - REQUIRED_ARG, 10, 1, LONG_TIMEOUT, 0, 1, 0}, + "Log all queries that have taken more than long_query_time seconds to execute to file. " + "The argument will be treated as a decimal value with microsecond precission.", + (uchar**) &long_query_time, (uchar**) &long_query_time, 0, GET_DOUBLE, + REQUIRED_ARG, 10, 0, LONG_TIMEOUT, 0, 0, 0}, {"lower_case_table_names", OPT_LOWER_CASE_TABLE_NAMES, "If set to 1 table names are stored in lowercase on disk and table names will be case-insensitive. Should be set to 2 if you are using a case insensitive file system", (uchar**) &lower_case_table_names, @@ -6023,40 +6041,6 @@ log and this option does nothing anymore.", 0 #endif , 0, 2, 0, 1, 0}, -#ifdef WITH_MARIA_STORAGE_ENGINE - {"maria_block_size", OPT_MARIA_BLOCK_SIZE, - "Block size to be used for MARIA index pages.", - (uchar**) &maria_block_size, - (uchar**) &maria_block_size, 0, GET_ULONG, REQUIRED_ARG, - MARIA_KEY_BLOCK_LENGTH, MARIA_MIN_KEY_BLOCK_LENGTH, - MARIA_MAX_KEY_BLOCK_LENGTH, - 0, MARIA_MIN_KEY_BLOCK_LENGTH, 0}, - {"maria_max_sort_file_size", OPT_MARIA_MAX_SORT_FILE_SIZE, - "Don't use the fast sort index method to created index if the temporary " - "file would get bigger than this.", - (uchar**) &global_system_variables.maria_max_sort_file_size, - (uchar**) &max_system_variables.maria_max_sort_file_size, 0, - GET_ULL, REQUIRED_ARG, (longlong) LONG_MAX, 0, (ulonglong) MAX_FILE_SIZE, - 0, 1024*1024, 0}, - {"maria_repair_threads", OPT_MARIA_REPAIR_THREADS, - "Number of threads to use when repairing maria tables. The value of 1 " - "disables parallel repair.", - (uchar**) &global_system_variables.maria_repair_threads, - (uchar**) &max_system_variables.maria_repair_threads, 0, - GET_ULONG, REQUIRED_ARG, 1, 1, ~0L, 0, 1, 0}, - {"maria_sort_buffer_size", OPT_MARIA_SORT_BUFFER_SIZE, - "The buffer that is allocated when sorting the index when doing a REPAIR " - "or when creating indexes with CREATE INDEX or ALTER TABLE.", - (uchar**) &global_system_variables.maria_sort_buff_size, - (uchar**) &max_system_variables.maria_sort_buff_size, 0, - GET_ULONG, REQUIRED_ARG, 8192*1024, 4, ~0L, 0, 1, 0}, - {"maria_stats_method", OPT_MARIA_STATS_METHOD, - "Specifies how maria index statistics collection code should threat NULLs. " - "Possible values of name are \"nulls_unequal\" (default behavior for 4.1/5.0), " - "\"nulls_equal\" (emulate 4.0 behavior), and \"nulls_ignored\".", - (uchar**) &maria_stats_method_str, (uchar**) &maria_stats_method_str, 0, - GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, -#endif {"max_allowed_packet", OPT_MAX_ALLOWED_PACKET, "Max packetlength to send/receive from to server.", (uchar**) &global_system_variables.max_allowed_packet, @@ -6144,6 +6128,11 @@ The minimum value for this variable is 4096.", "After this many write locks, allow some read locks to run in between.", (uchar**) &max_write_lock_count, (uchar**) &max_write_lock_count, 0, GET_ULONG, REQUIRED_ARG, ~0L, 1, ~0L, 0, 1, 0}, + {"min_examined_row_limit", OPT_MIN_EXAMINED_ROW_LIMIT, + "Don't log queries which examine less than min_examined_row_limit rows to file.", + (uchar**) &global_system_variables.min_examined_row_limit, + (uchar**) &max_system_variables.min_examined_row_limit, 0, GET_ULONG, + REQUIRED_ARG, 0, 0, ~0L, 0, 1L, 0}, {"multi_range_count", OPT_MULTI_RANGE_COUNT, "Number of key ranges to request at once.", (uchar**) &global_system_variables.multi_range_count, @@ -6408,7 +6397,8 @@ The minimum value for this variable is 4096.", (uchar**) &opt_date_time_formats[MYSQL_TIMESTAMP_TIME], 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"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.", + "If an internal in-memory temporary table exceeds this size, MySQL will" + " automatically convert it to an on-disk MyISAM table.", (uchar**) &global_system_variables.tmp_table_size, (uchar**) &max_system_variables.tmp_table_size, 0, GET_ULL, REQUIRED_ARG, 16*1024*1024L, 1024, MAX_MEM_TABLE_SIZE, 0, 1, 0}, @@ -6946,6 +6936,7 @@ SHOW_VAR status_vars[]= { {"Open_streams", (char*) &my_stream_opened, SHOW_LONG_NOFLUSH}, {"Open_table_definitions", (char*) &show_table_definitions, SHOW_FUNC}, {"Open_tables", (char*) &show_open_tables, SHOW_FUNC}, + {"Opened_files", (char*) &my_file_total_opened, SHOW_LONG_NOFLUSH}, {"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 @@ -7047,7 +7038,7 @@ Starts the MySQL database server\n"); printf("Usage: %s [OPTIONS]\n", my_progname); if (!opt_verbose) - puts("\nFor more help options (several pages), use mysqld --verbose --help\n"); + puts("\nFor more help options (several pages), use mysqld --verbose --help"); else { #ifdef __WIN__ @@ -7072,7 +7063,7 @@ Starts the MySQL database server\n"); puts("\n\ To see what values a running MySQL server is using, type\n\ -'mysqladmin variables' instead of 'mysqld --verbose --help'.\n"); +'mysqladmin variables' instead of 'mysqld --verbose --help'."); } } #endif /*!EMBEDDED_LIBRARY*/ @@ -7164,7 +7155,7 @@ static void mysql_init_variables(void) global_query_id= thread_id= 1L; strmov(server_version, MYSQL_SERVER_VERSION); myisam_recover_options_str= sql_mode_str= "OFF"; - myisam_stats_method_str= maria_stats_method_str= "nulls_unequal"; + myisam_stats_method_str= "nulls_unequal"; my_bind_addr = htonl(INADDR_ANY); threads.empty(); thread_cache.empty(); @@ -7199,7 +7190,7 @@ static void mysql_init_variables(void) master_password= master_host= 0; master_info_file= (char*) "master.info", relay_log_info_file= (char*) "relay-log.info"; - master_ssl_key= master_ssl_cert= master_ssl_ca= + master_ssl_key= master_ssl_cert= master_ssl_ca= master_ssl_capath= master_ssl_cipher= 0; report_user= report_password = report_host= 0; /* TO BE DELETED */ opt_relay_logname= opt_relaylog_index_name= 0; @@ -7227,7 +7218,6 @@ static void mysql_init_variables(void) when collecting index statistics for MyISAM tables. */ global_system_variables.myisam_stats_method= MI_STATS_METHOD_NULLS_NOT_EQUAL; - global_system_variables.maria_stats_method= MI_STATS_METHOD_NULLS_NOT_EQUAL; /* Variables that depends on compile options */ #ifndef DBUG_OFF @@ -7326,9 +7316,10 @@ static void mysql_init_variables(void) } -static my_bool -get_one_option(int optid, const struct my_option *opt __attribute__((unused)), - char *argument) +my_bool +mysqld_get_one_option(int optid, + const struct my_option *opt __attribute__((unused)), + char *argument) { switch(optid) { case '#': @@ -7727,11 +7718,16 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), #endif case OPT_MYISAM_RECOVER: { - if (!argument || !argument[0]) + if (!argument) { myisam_recover_options= HA_RECOVER_DEFAULT; myisam_recover_options_str= myisam_recover_typelib.type_names[0]; } + else if (!argument[0]) + { + myisam_recover_options= HA_RECOVER_NONE; + myisam_recover_options_str= "OFF"; + } else { myisam_recover_options_str=argument; @@ -7814,9 +7810,14 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), } return 0; } - /* Initiates DEBUG - but no debugging here ! */ -static uchar* * + +/* Handle arguments for multiple key caches */ + +extern "C" uchar **mysql_getopt_value(const char *keyname, uint key_length, + const struct my_option *option); + +uchar* * mysql_getopt_value(const char *keyname, uint key_length, const struct my_option *option) { @@ -7863,11 +7864,19 @@ mysql_getopt_value(const char *keyname, uint key_length, } -static void option_error_reporter(enum loglevel level, const char *format, ...) +extern "C" void option_error_reporter(enum loglevel level, const char *format, ...); + +void option_error_reporter(enum loglevel level, const char *format, ...) { va_list args; va_start(args, format); - vprint_msg_to_log(level, format, args); + + /* Don't print warnings for --loose options during bootstrap */ + if (level == ERROR_LEVEL || !opt_bootstrap || + global_system_variables.log_warnings) + { + vprint_msg_to_log(level, format, args); + } va_end(args); } @@ -7885,14 +7894,15 @@ static void get_options(int *argc,char **argv) my_getopt_skip_unknown= TRUE; if ((ho_error= handle_options(argc, &argv, my_long_options, - get_one_option))) + mysqld_get_one_option))) exit(ho_error); (*argc)++; /* add back one for the progname handle_options removes */ /* no need to do this for argv as we are discarding it. */ - if ((opt_log_slow_admin_statements || opt_log_queries_not_using_indexes) && + if ((opt_log_slow_admin_statements || opt_log_queries_not_using_indexes || + opt_log_slow_slave_statements) && !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"); + sql_print_warning("options --log-slow-admin-statements, --log-queries-not-using-indexes and --log-slow-slave-statements have no effect if --log-slow-queries is not set"); #if defined(HAVE_BROKEN_REALPATH) my_use_symdir=0; @@ -7936,10 +7946,12 @@ static void get_options(int *argc,char **argv) /* Set global variables based on startup options */ myisam_block_size=(uint) 1 << my_bit_log2(opt_myisam_block_size); + /* long_query_time is in microseconds */ + global_system_variables.long_query_time= max_system_variables.long_query_time= + (longlong) (long_query_time * 1000000.0); + if (opt_short_log_format) opt_specialflag|= SPECIAL_SHORT_LOG_FORMAT; - if (opt_log_queries_not_using_indexes) - opt_specialflag|= SPECIAL_LOG_QUERIES_NOT_USING_INDEXES; if (init_global_datetime_format(MYSQL_TIMESTAMP_DATE, &global_system_variables.date_format) || @@ -8243,7 +8255,7 @@ void refresh_status(THD *thd) add_to_status(&global_status_var, &thd->status_var); /* Reset thread's status variables */ - bzero((char*) &thd->status_var, sizeof(thd->status_var)); + bzero((uchar*) &thd->status_var, sizeof(thd->status_var)); /* Reset some global variables */ reset_status_vars(); diff --git a/sql/net_serv.cc b/sql/net_serv.cc index bd273145782..1f75d7ab1d7 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -437,7 +437,7 @@ net_write_command(NET *net,uchar command, uchar buff[NET_HEADER_SIZE+1]; uint header_size=NET_HEADER_SIZE+1; DBUG_ENTER("net_write_command"); - DBUG_PRINT("enter",("length: %lu", len)); + DBUG_PRINT("enter",("length: %lu", (ulong) len)); buff[4]=command; /* For first packet */ diff --git a/sql/opt_range.cc b/sql/opt_range.cc index fbb1a2be487..97d0bcc8707 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1264,8 +1264,16 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler) thd= head->in_use; if (!(file= head->file->clone(thd->mem_root))) { + /* + Manually set the error flag. Note: there seems to be quite a few + places where a failure could cause the server to "hang" the client by + sending no response to a query. ATM those are not real errors because + the storage engine calls in question happen to never fail with the + existing storage engines. + */ + thd->net.report_error= 1; /* purecov: inspected */ /* Caller will free the memory */ - goto failure; + goto failure; /* purecov: inspected */ } head->column_bitmaps_set(&column_bitmap, &column_bitmap); @@ -1748,10 +1756,10 @@ SEL_ARG *SEL_ARG::clone_tree(RANGE_OPT_PARAM *param) the UPDATE/DELETE code will work: * index can only be scanned in forward direction * HA_EXTRA_KEYREAD will not be used - Perhaps these assumptions could be relaxed + Perhaps these assumptions could be relaxed. RETURN - index number + Number of the index that produces the required ordering in the cheapest way MAX_KEY if no such index was found. */ @@ -1770,6 +1778,7 @@ uint get_index_for_order(TABLE *table, ORDER *order, ha_rows limit) if (!(table->keys_in_use_for_query.is_set(idx))) continue; KEY_PART_INFO *keyinfo= table->key_info[idx].key_part; + uint n_parts= table->key_info[idx].key_parts; uint partno= 0; /* @@ -1779,7 +1788,7 @@ uint get_index_for_order(TABLE *table, ORDER *order, ha_rows limit) */ if (!(table->file->index_flags(idx, 0, 1) & HA_READ_ORDER)) continue; - for (ord= order; ord; ord= ord->next, partno++) + for (ord= order; ord && partno < n_parts; ord= ord->next, partno++) { Item *item= order->item[0]; if (!(item->type() == Item::FIELD_ITEM && @@ -3494,7 +3503,7 @@ double get_sweep_read_cost(const PARAM *param, ha_rows records) if (param->table->file->primary_key_is_clustered()) { result= param->table->file->read_time(param->table->s->primary_key, - records, records); + (uint)records, records); } else { @@ -3703,7 +3712,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge, /* Add Unique operations cost */ unique_calc_buff_size= - Unique::get_cost_calc_buff_size(non_cpk_scan_records, + Unique::get_cost_calc_buff_size((ulong)non_cpk_scan_records, param->table->file->ref_length, param->thd->variables.sortbuff_size); if (param->imerge_cost_buff_size < unique_calc_buff_size) @@ -3715,7 +3724,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge, } imerge_cost += - Unique::get_use_cost(param->imerge_cost_buff, non_cpk_scan_records, + Unique::get_use_cost(param->imerge_cost_buff, (uint)non_cpk_scan_records, param->table->file->ref_length, param->thd->variables.sortbuff_size); DBUG_PRINT("info",("index_merge total cost: %g (wanted: less then %g)", @@ -4055,7 +4064,7 @@ ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param) info->is_covering= FALSE; info->index_scan_costs= 0.0; info->index_records= 0; - info->out_rows= param->table->file->stats.records; + info->out_rows= (double) param->table->file->stats.records; bitmap_clear_all(&info->covered_fields); return info; } @@ -7240,6 +7249,11 @@ check_quick_select(PARAM *param,uint idx,SEL_ARG *tree, bool update_tbl_stats) param->is_ror_scan is cleared if the function detects that the key scan is not a Rowid-Ordered Retrieval scan ( see comments for is_key_scan_ror function for description of which key scans are ROR scans) + + RETURN + #records E(#records) for given subtree + HA_POS_ERROR if subtree cannot be used for record retrieval + */ static ha_rows @@ -7441,27 +7455,24 @@ check_quick_keys(PARAM *param, uint idx, SEL_ARG *key_tree, ROR (Rowid Ordered Retrieval) key scan is a key scan that produces ordered sequence of rowids (ha_xxx::cmp_ref is the comparison function) - An index scan is a ROR scan if it is done using a condition in form + This function is needed to handle a practically-important special case: + an index scan is a ROR scan if it is done using a condition in form - "key1_1=c_1 AND ... AND key1_n=c_n" (1) + "key1_1=c_1 AND ... AND key1_n=c_n" where the index is defined on (key1_1, ..., key1_N [,a_1, ..., a_n]) - and the table has a clustered Primary Key - - PRIMARY KEY(a_1, ..., a_n, b1, ..., b_k) with first key parts being - identical to uncovered parts ot the key being scanned (2) - - Scans on HASH indexes are not ROR scans, - any range scan on clustered primary key is ROR scan (3) + and the table has a clustered Primary Key defined as - Check (1) is made in check_quick_keys() - Check (3) is made check_quick_select() - Check (2) is made by this function. + PRIMARY KEY(a_1, ..., a_n, b1, ..., b_k) + + i.e. the first key parts of it are identical to uncovered parts ot the + key being scanned. This function assumes that the index flags do not + include HA_KEY_SCAN_NOT_ROR flag (that is checked elsewhere). RETURN - TRUE If the scan is ROR-scan - FALSE otherwise + TRUE The scan is ROR-scan + FALSE Otherwise */ static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts) @@ -7801,8 +7812,7 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, range->min_keypart_map= range->max_keypart_map= make_prev_keypart_map(ref->key_parts); range->flag= ((ref->key_length == key_info->key_length && - (key_info->flags & (HA_NOSAME | HA_END_SPACE_KEY)) == - HA_NOSAME) ? EQ_RANGE : 0); + (key_info->flags & HA_END_SPACE_KEY) == 0) ? EQ_RANGE : 0); if (!(quick->key_parts=key_part=(KEY_PART *) alloc_root(&quick->alloc,sizeof(KEY_PART)*ref->key_parts))) @@ -8182,7 +8192,7 @@ int QUICK_RANGE_SELECT::reset() if (file->ha_table_flags() & HA_NEED_READ_RANGE_BUFFER) { mrange_bufsiz= min(multi_range_bufsiz, - (QUICK_SELECT_I::records + 1)* head->s->reclength); + ((uint)QUICK_SELECT_I::records + 1)* head->s->reclength); while (mrange_bufsiz && ! my_multi_malloc(MYF(MY_WME), @@ -8361,7 +8371,8 @@ int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length, { /* Read the next record in the same range with prefix after cur_prefix. */ DBUG_ASSERT(cur_prefix != 0); - result= file->index_read(record, cur_prefix, keypart_map, HA_READ_AFTER_KEY); + result= file->index_read_map(record, cur_prefix, keypart_map, + HA_READ_AFTER_KEY); if (result || (file->compare_key(file->end_range) <= 0)) DBUG_RETURN(result); } @@ -8432,9 +8443,10 @@ int QUICK_RANGE_SELECT_GEOM::get_next() } last_range= *(cur_range++); - result= file->index_read(record, last_range->min_key, - last_range->min_keypart_map, - (ha_rkey_function)(last_range->flag ^ GEOM_FLAG)); + result= file->index_read_map(record, last_range->min_key, + last_range->min_keypart_map, + (ha_rkey_function)(last_range->flag ^ + GEOM_FLAG)); if (result != HA_ERR_KEY_NOT_FOUND && result != HA_ERR_END_OF_FILE) DBUG_RETURN(result); last_range= 0; // Not found, to next range @@ -8565,18 +8577,19 @@ int QUICK_SELECT_DESC::get_next() if (last_range->flag & EQ_RANGE) { - result = file->index_read(record, last_range->max_key, - last_range->max_keypart_map, HA_READ_KEY_EXACT); + result = file->index_read_map(record, last_range->max_key, + last_range->max_keypart_map, + HA_READ_KEY_EXACT); } else { DBUG_ASSERT(last_range->flag & NEAR_MAX || range_reads_after_key(last_range)); - result=file->index_read(record, last_range->max_key, - last_range->max_keypart_map, - ((last_range->flag & NEAR_MAX) ? - HA_READ_BEFORE_KEY : - HA_READ_PREFIX_LAST_OR_PREV)); + result=file->index_read_map(record, last_range->max_key, + last_range->max_keypart_map, + ((last_range->flag & NEAR_MAX) ? + HA_READ_BEFORE_KEY : + HA_READ_PREFIX_LAST_OR_PREV)); } if (result) { @@ -9805,7 +9818,7 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts, bool have_min, bool have_max, double *read_cost, ha_rows *records) { - uint table_records; + ha_rows table_records; uint num_groups; uint num_blocks; uint keys_per_block; @@ -9822,14 +9835,14 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts, keys_per_block= (table->file->stats.block_size / 2 / (index_info->key_length + table->file->ref_length) + 1); - num_blocks= (table_records / keys_per_block) + 1; + num_blocks= (uint)(table_records / keys_per_block) + 1; /* Compute the number of keys in a group. */ keys_per_group= index_info->rec_per_key[group_key_parts - 1]; if (keys_per_group == 0) /* If there is no statistics try to guess */ /* each group contains 10% of all records */ - keys_per_group= (table_records / 10) + 1; - num_groups= (table_records / keys_per_group) + 1; + keys_per_group= (uint)(table_records / 10) + 1; + num_groups= (uint)(table_records / keys_per_group) + 1; /* Apply the selectivity of the quick select for group prefixes. */ if (range_tree && (quick_prefix_records != HA_POS_ERROR)) @@ -9873,9 +9886,9 @@ 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: %lu blocks: %u", - table_records, keys_per_block, keys_per_group, (ulong) *records, - num_blocks)); + ("table rows: %lu keys/block: %u keys/group: %u result rows: %lu blocks: %u", + (ulong)table_records, keys_per_block, keys_per_group, + (ulong) *records, num_blocks)); DBUG_VOID_RETURN; } @@ -10432,9 +10445,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::get_next() first sub-group with the extended prefix. */ if (!have_min && !have_max && key_infix_len > 0) - result= file->index_read(record, group_prefix, - make_prev_keypart_map(real_key_parts), - HA_READ_KEY_EXACT); + result= file->index_read_map(record, group_prefix, + make_prev_keypart_map(real_key_parts), + HA_READ_KEY_EXACT); result= have_min ? min_res : have_max ? max_res : result; } while ((result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE) && @@ -10496,9 +10509,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min() /* Apply the constant equality conditions to the non-group select fields */ if (key_infix_len > 0) { - if ((result= file->index_read(record, group_prefix, - make_prev_keypart_map(real_key_parts), - HA_READ_KEY_EXACT))) + if ((result= file->index_read_map(record, group_prefix, + make_prev_keypart_map(real_key_parts), + HA_READ_KEY_EXACT))) DBUG_RETURN(result); } @@ -10513,9 +10526,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min() { /* Find the first subsequent record without NULL in the MIN/MAX field. */ key_copy(tmp_record, record, index_info, 0); - result= file->index_read(record, tmp_record, - make_keypart_map(real_key_parts), - HA_READ_AFTER_KEY); + result= file->index_read_map(record, tmp_record, + make_keypart_map(real_key_parts), + HA_READ_AFTER_KEY); /* Check if the new record belongs to the current group by comparing its prefix with the group's prefix. If it is from the next group, then the @@ -10570,9 +10583,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_max() if (min_max_ranges.elements > 0) result= next_max_in_range(); else - result= file->index_read(record, group_prefix, - make_prev_keypart_map(real_key_parts), - HA_READ_PREFIX_LAST); + result= file->index_read_map(record, group_prefix, + make_prev_keypart_map(real_key_parts), + HA_READ_PREFIX_LAST); DBUG_RETURN(result); } @@ -10623,9 +10636,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_prefix() else { /* Load the first key in this group into record. */ - result= file->index_read(record, group_prefix, - make_prev_keypart_map(group_key_parts), - HA_READ_AFTER_KEY); + result= file->index_read_map(record, group_prefix, + make_prev_keypart_map(group_key_parts), + HA_READ_AFTER_KEY); if (result) DBUG_RETURN(result); } @@ -10702,7 +10715,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min_in_range() HA_READ_AFTER_KEY : HA_READ_KEY_OR_NEXT; } - result= file->index_read(record, group_prefix, keypart_map, find_flag); + result= file->index_read_map(record, group_prefix, keypart_map, find_flag); if (result) { if ((result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE) && @@ -10835,7 +10848,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_max_in_range() HA_READ_BEFORE_KEY : HA_READ_PREFIX_LAST_OR_PREV; } - result= file->index_read(record, group_prefix, keypart_map, find_flag); + result= file->index_read_map(record, group_prefix, keypart_map, find_flag); if (result) { diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 8851401d21f..6836c53db4e 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -270,9 +270,10 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) Closed interval: Either The MIN argument is non-nullable, or we have a >= predicate for the MIN argument. */ - error= table->file->index_read(table->record[0], ref.key_buff, - make_prev_keypart_map(ref.key_parts), - HA_READ_KEY_OR_NEXT); + error= table->file->index_read_map(table->record[0], + ref.key_buff, + make_prev_keypart_map(ref.key_parts), + HA_READ_KEY_OR_NEXT); else { /* @@ -281,9 +282,10 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) 2) there is a > predicate on it, nullability is irrelevant. We need to scan the next bigger record first. */ - error= table->file->index_read(table->record[0], ref.key_buff, - make_prev_keypart_map(ref.key_parts), - HA_READ_AFTER_KEY); + error= table->file->index_read_map(table->record[0], + ref.key_buff, + make_prev_keypart_map(ref.key_parts), + HA_READ_AFTER_KEY); /* If the found record is outside the group formed by the search prefix, or there is no such record at all, check if all @@ -305,9 +307,10 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) key_cmp_if_same(table, ref.key_buff, ref.key, prefix_len))) { DBUG_ASSERT(item_field->field->real_maybe_null()); - error= table->file->index_read(table->record[0], ref.key_buff, - make_prev_keypart_map(ref.key_parts), - HA_READ_KEY_EXACT); + error= table->file->index_read_map(table->record[0], + ref.key_buff, + make_prev_keypart_map(ref.key_parts), + HA_READ_KEY_EXACT); } } } @@ -394,11 +397,11 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) if (!ref.key_length) error= table->file->index_last(table->record[0]); else - error= table->file->index_read(table->record[0], key_buff, - make_prev_keypart_map(ref.key_parts), - range_fl & NEAR_MAX ? - HA_READ_BEFORE_KEY : - HA_READ_PREFIX_LAST_OR_PREV); + error= table->file->index_read_map(table->record[0], key_buff, + make_prev_keypart_map(ref.key_parts), + range_fl & NEAR_MAX ? + HA_READ_BEFORE_KEY : + HA_READ_PREFIX_LAST_OR_PREV); if (!error && reckey_in_range(1, &ref, item_field->field, conds, range_fl, prefix_len)) error= HA_ERR_KEY_NOT_FOUND; diff --git a/sql/parse_file.cc b/sql/parse_file.cc index 5ea49396cb4..19fb11ac0cc 100644 --- a/sql/parse_file.cc +++ b/sql/parse_file.cc @@ -135,7 +135,7 @@ write_parameter(IO_CACHE *file, uchar* base, File_option *parameter, { /* string have to be allocated already */ LEX_STRING *val_s= (LEX_STRING *)(base + parameter->offset); - time_t tm= time(NULL); + time_t tm= my_time(0); get_date(val_s->str, GETDATE_DATE_TIME|GETDATE_GMT|GETDATE_FIXEDLENGTH, tm); diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 23bc3c96e8f..ab887d5dda0 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -91,10 +91,10 @@ partition_info *partition_info::get_clone() #define MAX_PART_NAME_SIZE 8 char *partition_info::create_default_partition_names(uint part_no, - uint no_parts, + uint no_parts_arg, uint start_no) { - char *ptr= (char*) sql_calloc(no_parts*MAX_PART_NAME_SIZE); + char *ptr= (char*) sql_calloc(no_parts_arg*MAX_PART_NAME_SIZE); char *move_ptr= ptr; uint i= 0; DBUG_ENTER("create_default_partition_names"); @@ -105,11 +105,11 @@ char *partition_info::create_default_partition_names(uint part_no, { my_sprintf(move_ptr, (move_ptr,"p%u", (start_no + i))); move_ptr+=MAX_PART_NAME_SIZE; - } while (++i < no_parts); + } while (++i < no_parts_arg); } else { - mem_alloc_error(no_parts*MAX_PART_NAME_SIZE); + mem_alloc_error(no_parts_arg*MAX_PART_NAME_SIZE); } DBUG_RETURN(ptr); } @@ -905,7 +905,6 @@ bool partition_info::set_up_charset_field_preps() Field *field, **ptr; uchar **char_ptrs; unsigned i; - bool found; size_t size; uint tot_fields= 0; uint tot_part_fields= 0; @@ -918,7 +917,6 @@ bool partition_info::set_up_charset_field_preps() { ptr= part_field_array; /* Set up arrays and buffers for those fields */ - i= 0; while ((field= *(ptr++))) { if (field_is_partition_charset(field)) @@ -954,7 +952,7 @@ bool partition_info::set_up_charset_field_preps() } part_charset_field_array[i]= NULL; } - if (is_sub_partitioned() && list_of_subpart_fields && + if (is_sub_partitioned() && !list_of_subpart_fields && check_part_func_fields(subpart_field_array, FALSE)) { /* Set up arrays and buffers for those fields */ @@ -962,7 +960,10 @@ bool partition_info::set_up_charset_field_preps() while ((field= *(ptr++))) { if (field_is_partition_charset(field)) + { tot_subpart_fields++; + tot_fields++; + } } size= tot_subpart_fields * sizeof(char*); if (!(char_ptrs= (uchar**) sql_calloc(size))) @@ -975,10 +976,10 @@ bool partition_info::set_up_charset_field_preps() if (!(char_ptrs= (uchar**) sql_alloc(size))) goto error; subpart_charset_field_array= (Field**)char_ptrs; + ptr= subpart_field_array; i= 0; while ((field= *(ptr++))) { - unsigned j= 0; CHARSET_INFO *cs; uchar *field_buf; LINT_INIT(field_buf); @@ -987,28 +988,16 @@ bool partition_info::set_up_charset_field_preps() continue; cs= ((Field_str*)field)->charset(); size= field->pack_length(); - found= FALSE; - for (j= 0; j < tot_part_fields; j++) - { - if (field == part_charset_field_array[i]) - found= TRUE; - } - if (!found) - { - tot_fields++; - if (!(field_buf= (uchar*) sql_calloc(size))) - goto error; - } + if (!(field_buf= (uchar*) sql_calloc(size))) + goto error; + subpart_charset_field_array[i]= field; subpart_field_buffers[i++]= field_buf; } - if (!(char_ptrs= (uchar**) sql_calloc(size))) - goto error; - restore_subpart_field_ptrs= char_ptrs; + subpart_charset_field_array[i]= NULL; } if (tot_fields) { - uint j,k,l; - + uint k; size= tot_fields*sizeof(char**); if (!(char_ptrs= (uchar**)sql_calloc(size))) goto error; @@ -1026,11 +1015,12 @@ bool partition_info::set_up_charset_field_preps() full_part_field_buffers[i]= part_field_buffers[i]; } k= tot_part_fields; - l= 0; for (i= 0; i < tot_subpart_fields; i++) { + uint j; + bool found= FALSE; field= subpart_charset_field_array[i]; - found= FALSE; + for (j= 0; j < tot_part_fields; j++) { if (field == part_charset_field_array[i]) @@ -1038,12 +1028,12 @@ bool partition_info::set_up_charset_field_preps() } if (!found) { - full_part_charset_field_array[l]= subpart_charset_field_array[k]; - full_part_field_buffers[l]= subpart_field_buffers[k]; - k++; l++; + full_part_charset_field_array[k]= subpart_charset_field_array[i]; + full_part_field_buffers[k]= subpart_field_buffers[i]; + k++; } } - full_part_charset_field_array[tot_fields]= NULL; + full_part_charset_field_array[k]= NULL; } DBUG_RETURN(FALSE); error: diff --git a/sql/partition_info.h b/sql/partition_info.h index 10edea074c0..b7d13e188f3 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -139,20 +139,6 @@ public: */ get_partitions_in_range_iter get_subpart_iter_for_interval; - /* - Valid iff - get_part_iter_for_interval=get_part_iter_for_interval_via_walking: - controls how we'll process "field < C" and "field > C" intervals. - If the partitioning function F is strictly increasing, then for any x, y - "x < y" => "F(x) < F(y)" (*), i.e. when we get interval "field < C" - we can perform partition pruning on the equivalent "F(field) < F(C)". - - If the partitioning function not strictly increasing (it is simply - increasing), then instead of (*) we get "x < y" => "F(x) <= F(y)" - i.e. for interval "field < C" we can perform partition pruning for - "F(field) <= F(C)". - */ - bool range_analysis_include_bounds; /******************************************** * INTERVAL ANALYSIS ENDS ********************************************/ diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 8bab1ff1ca3..834d87532af 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -492,7 +492,7 @@ bool show_new_master(THD* thd) 0 success */ -int update_slave_list(MYSQL* mysql, MASTER_INFO* mi) +int update_slave_list(MYSQL* mysql, Master_info* mi) { MYSQL_RES* res=0; MYSQL_ROW row; @@ -679,7 +679,7 @@ bool show_slave_hosts(THD* thd) } -int connect_to_master(THD *thd, MYSQL* mysql, MASTER_INFO* mi) +int connect_to_master(THD *thd, MYSQL* mysql, Master_info* mi) { DBUG_ENTER("connect_to_master"); @@ -728,7 +728,7 @@ static inline void cleanup_mysql_results(MYSQL_RES* db_res, static int fetch_db_tables(THD *thd, MYSQL *mysql, const char *db, - MYSQL_RES *table_res, MASTER_INFO *mi) + MYSQL_RES *table_res, Master_info *mi) { MYSQL_ROW row; for (row = mysql_fetch_row(table_res); row; diff --git a/sql/repl_failsafe.h b/sql/repl_failsafe.h index 561db00d841..6ff78067aca 100644 --- a/sql/repl_failsafe.h +++ b/sql/repl_failsafe.h @@ -33,12 +33,12 @@ extern const char* rpl_role_type[], *rpl_status_type[]; pthread_handler_t handle_failsafe_rpl(void *arg); void change_rpl_status(RPL_STATUS from_status, RPL_STATUS to_status); int find_recovery_captain(THD* thd, MYSQL* mysql); -int update_slave_list(MYSQL* mysql, MASTER_INFO* mi); +int update_slave_list(MYSQL* mysql, Master_info* mi); extern HASH slave_list; bool load_master_data(THD* thd); -int connect_to_master(THD *thd, MYSQL* mysql, MASTER_INFO* mi); +int connect_to_master(THD *thd, MYSQL* mysql, Master_info* mi); bool show_new_master(THD* thd); bool show_slave_hosts(THD* thd); diff --git a/sql/rpl_filter.cc b/sql/rpl_filter.cc index bb5db0106eb..fb609e12dcb 100644 --- a/sql/rpl_filter.cc +++ b/sql/rpl_filter.cc @@ -362,9 +362,11 @@ Rpl_filter::add_ignore_db(const char* table_spec) ignore_db.push_back(db); } +extern "C" uchar *get_table_key(const uchar *, size_t *, my_bool); +extern "C" void free_table_ent(void* a); -static uchar* get_table_key(const uchar* a, size_t *len, - my_bool __attribute__((unused))) +uchar *get_table_key(const uchar* a, size_t *len, + my_bool __attribute__((unused))) { TABLE_RULE_ENT *e= (TABLE_RULE_ENT *) a; @@ -373,7 +375,7 @@ static uchar* get_table_key(const uchar* a, size_t *len, } -static void free_table_ent(void* a) +void free_table_ent(void* a) { TABLE_RULE_ENT *e= (TABLE_RULE_ENT *) a; diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index f506a3c319d..1165164fd88 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -27,7 +27,7 @@ int init_intvar_from_file(int* var, IO_CACHE* f, int default_val); int init_strvar_from_file(char *var, int max_size, IO_CACHE *f, const char *default_val); -MASTER_INFO::MASTER_INFO() +Master_info::Master_info() :Slave_reporting_capability("I/O"), ssl(0), fd(-1), io_thd(0), inited(0), abort_slave(0),slave_running(0), @@ -45,7 +45,7 @@ MASTER_INFO::MASTER_INFO() pthread_cond_init(&stop_cond, NULL); } -MASTER_INFO::~MASTER_INFO() +Master_info::~Master_info() { pthread_mutex_destroy(&run_lock); pthread_mutex_destroy(&data_lock); @@ -55,7 +55,7 @@ MASTER_INFO::~MASTER_INFO() } -void init_master_info_with_options(MASTER_INFO* mi) +void init_master_info_with_options(Master_info* mi) { DBUG_ENTER("init_master_info_with_options"); @@ -98,7 +98,7 @@ enum { LINES_IN_MASTER_INFO= LINE_FOR_MASTER_SSL_VERIFY_SERVER_CERT }; -int init_master_info(MASTER_INFO* mi, const char* master_info_fname, +int init_master_info(Master_info* mi, const char* master_info_fname, const char* slave_info_fname, bool abort_if_no_master_info_file, int thread_mask) @@ -338,7 +338,7 @@ err: 1 - flush master info failed 0 - all ok */ -int flush_master_info(MASTER_INFO* mi, bool flush_relay_log_cache) +int flush_master_info(Master_info* mi, bool flush_relay_log_cache) { IO_CACHE* file = &mi->file; char lbuf[22]; @@ -392,7 +392,7 @@ int flush_master_info(MASTER_INFO* mi, bool flush_relay_log_cache) } -void end_master_info(MASTER_INFO* mi) +void end_master_info(Master_info* mi) { DBUG_ENTER("end_master_info"); diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h index 44ab0d1dc15..93fb0a98198 100644 --- a/sql/rpl_mi.h +++ b/sql/rpl_mi.h @@ -26,13 +26,13 @@ Replication IO Thread - MASTER_INFO contains: + Master_info contains: - information about how to connect to a master - current master log name - current master log offset - misc control variables - MASTER_INFO is initialized once from the master.info file if such + Master_info is initialized once from the master.info file if such exists. Otherwise, data members corresponding to master.info fields are initialized with defaults specified by master-* options. The initialization is done through init_master_info() call. @@ -55,11 +55,11 @@ *****************************************************************************/ -class MASTER_INFO : public Slave_reporting_capability +class Master_info : public Slave_reporting_capability { public: - MASTER_INFO(); - ~MASTER_INFO(); + Master_info(); + ~Master_info(); /* the variables below are needed because we can change masters on the fly */ char master_log_name[FN_REFLEN]; @@ -80,7 +80,7 @@ class MASTER_INFO : public Slave_reporting_capability THD *io_thd; MYSQL* mysql; uint32 file_id; /* for 3.23 load data infile */ - RELAY_LOG_INFO rli; + Relay_log_info rli; uint port; uint connect_retry; #ifndef DBUG_OFF @@ -102,13 +102,13 @@ class MASTER_INFO : public Slave_reporting_capability long clock_diff_with_master; }; -void init_master_info_with_options(MASTER_INFO* mi); -int init_master_info(MASTER_INFO* mi, const char* master_info_fname, +void init_master_info_with_options(Master_info* mi); +int init_master_info(Master_info* mi, const char* master_info_fname, const char* slave_info_fname, bool abort_if_no_master_info_file, int thread_mask); -void end_master_info(MASTER_INFO* mi); -int flush_master_info(MASTER_INFO* mi, bool flush_relay_log_cache); +void end_master_info(Master_info* mi); +int flush_master_info(Master_info* mi, bool flush_relay_log_cache); #endif /* HAVE_REPLICATION */ #endif /* RPL_MI_H */ diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc index 94778948bf9..65c8e106112 100644 --- a/sql/rpl_record.cc +++ b/sql/rpl_record.cc @@ -17,6 +17,8 @@ #include "rpl_rli.h" #include "rpl_record.h" #include "slave.h" // Need to pull in slave_print_msg +#include "rpl_utility.h" +#include "rpl_rli.h" /** Pack a record of data for a table into a format suitable for @@ -90,8 +92,30 @@ pack_row(TABLE *table, MY_BITMAP const* cols, /* We only store the data of the field if it is non-null - */ + + For big-endian machines, we have to make sure that the + length is stored in little-endian format, since this is the + format used for the binlog. + + We do this by setting the db_low_byte_first, which is used + inside some store_length() to decide what order to write the + bytes in. + + In reality, db_log_byte_first is only set for legacy table + type Isam, but in the event of a bug, we need to guarantee + the endianess when writing to the binlog. + + This is currently broken for NDB due to BUG#29549, so we + will fix it when NDB has fixed their way of handling BLOBs. + */ +#if 0 + bool save= table->s->db_low_byte_first; + table->s->db_low_byte_first= TRUE; +#endif pack_ptr= field->pack(pack_ptr, field->ptr + offset); +#if 0 + table->s->db_low_byte_first= save; +#endif } null_mask <<= 1; @@ -133,9 +157,8 @@ pack_row(TABLE *table, MY_BITMAP const* cols, 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 @c arr and @c bits; the other parts of the - record are left alone. + The row is assumed to only consist of the fields for which the corresponding + bit in bitset @c cols is set; the other parts of the record are left alone. At most @c colcnt columns are read: if the table is larger than that, the remaining fields are not filled in. @@ -143,16 +166,14 @@ pack_row(TABLE *table, MY_BITMAP const* cols, @param rli Relay log info @param table Table to unpack into @param colcnt Number of columns to read from record - @param row Packed row data - @param cols Pointer to columns data to fill in + @param row_data + Packed row data + @param cols Pointer to bitset describing columns to fill in @param row_end Pointer to variable that will hold the value of the one-after-end position for the row @param master_reclength Pointer to variable that will be set to the length of the record on the master side - @param rw_set Pointer to bitmap that holds either the read_set or the - write_set of the table - @retval 0 No error @@ -163,11 +184,10 @@ pack_row(TABLE *table, MY_BITMAP const* cols, */ #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) int -unpack_row(RELAY_LOG_INFO const *rli, +unpack_row(Relay_log_info const *rli, TABLE *table, uint const colcnt, uchar const *const row_data, MY_BITMAP const *cols, - uchar const **const row_end, ulong *const master_reclength, - MY_BITMAP* const rw_set, Log_event_type const event_type) + uchar const **const row_end, ulong *const master_reclength) { DBUG_ENTER("unpack_row"); DBUG_ASSERT(row_data); @@ -177,10 +197,6 @@ unpack_row(RELAY_LOG_INFO const *rli, uchar const *null_ptr= row_data; uchar const *pack_ptr= row_data + master_null_byte_count; - bitmap_clear_all(rw_set); - - empty_record(table); - Field **const begin_ptr = table->field; Field **field_ptr; Field **const end_ptr= begin_ptr + colcnt; @@ -191,7 +207,9 @@ unpack_row(RELAY_LOG_INFO const *rli, unsigned int null_mask= 1U; // The "current" null bits unsigned int null_bits= *null_ptr++; - for (field_ptr= begin_ptr ; field_ptr < end_ptr ; ++field_ptr) + uint i= 0; + table_def *tabledef= ((Relay_log_info*)rli)->get_tabledef(table); + for (field_ptr= begin_ptr ; field_ptr < end_ptr && *field_ptr ; ++field_ptr) { Field *const f= *field_ptr; @@ -220,12 +238,47 @@ unpack_row(RELAY_LOG_INFO const *rli, f->set_notnull(); /* - We only unpack the field if it was non-null + We only unpack the field if it was non-null. + Use the master's size information if available else call + normal unpack operation. */ - pack_ptr= f->unpack(f->ptr, pack_ptr); +#if 0 + bool save= table->s->db_low_byte_first; + table->s->db_low_byte_first= TRUE; +#endif + uint16 const metadata= tabledef->field_metadata(i); + if (tabledef && metadata) + pack_ptr= f->unpack(f->ptr, pack_ptr, metadata); + else + pack_ptr= f->unpack(f->ptr, pack_ptr); +#if 0 + table->s->db_low_byte_first= save; +#endif + } + + null_mask <<= 1; + } + i++; + } + + /* + throw away master's extra fields + */ + uint max_cols= min(tabledef->size(), cols->n_bits); + for (; i < max_cols; i++) + { + if (bitmap_is_set(cols, i)) + { + if ((null_mask & 0xFF) == 0) + { + DBUG_ASSERT(null_ptr < row_data + master_null_byte_count); + null_mask= 1U; + null_bits= *null_ptr++; } + DBUG_ASSERT(null_mask & 0xFF); // One of the 8 LSB should be set - bitmap_set_bit(rw_set, f->field_index); + if (!((null_bits & null_mask) && tabledef->maybe_null(i))) + pack_ptr+= tabledef->calc_field_size(i, (uchar *) pack_ptr); null_mask <<= 1; } } @@ -244,30 +297,58 @@ unpack_row(RELAY_LOG_INFO const *rli, else *master_reclength = table->s->reclength; } + + DBUG_RETURN(error); +} - /* - Set properties for remaining columns, if there are any. We let the - corresponding bit in the write_set be set, to write the value if - it was not there already. We iterate over all remaining columns, - even if there were an error, to get as many error messages as - possible. We are still able to return a pointer to the next row, - so redo that. - - This generation of error messages is only relevant when inserting - new rows. - */ - for ( ; *field_ptr ; ++field_ptr) +/** + Fills @c table->record[0] with default values. + + First @c empty_record() is called and then, additionally, fields are + initialized explicitly with a call to @c set_default(). + + For optimization reasons, the explicit initialization can be skipped for + first @c skip fields. This is useful if later we are going to fill these + fields from other source (e.g. from a Rows replication event). + + If @c check is true, fields are explicitly initialized only if they have + default value or can be NULL. Otherwise error is reported. + + @param log Used to report errors. + @param table Table whose record[0] buffer is prepared. + @param skip Number of columns for which default value initialization + should be skipped. + @param check Indicates if errors should be checked when setting default + values. + + @returns 0 on success. + */ +int prepare_record(const Slave_reporting_capability *const log, + TABLE *const table, + const uint skip, const bool check) +{ + DBUG_ENTER("prepare_record"); + + int error= 0; + empty_record(table); + + if (skip >= table->s->fields) // nothing to do + DBUG_RETURN(0); + + /* Explicit initialization of fields */ + + for (Field **field_ptr= table->field+skip ; *field_ptr ; ++field_ptr) { uint32 const mask= NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG; Field *const f= *field_ptr; - if (event_type == WRITE_ROWS_EVENT && - ((*field_ptr)->flags & mask) == mask) + if (check && ((f->flags & mask) == mask)) { - rli->report(ERROR_LEVEL, ER_NO_DEFAULT_FOR_FIELD, + DBUG_ASSERT(log); + log->report(ERROR_LEVEL, ER_NO_DEFAULT_FOR_FIELD, "Field `%s` of table `%s`.`%s` " "has no default value and cannot be NULL", - (*field_ptr)->field_name, table->s->db.str, + f->field_name, table->s->db.str, table->s->table_name.str); error = ER_NO_DEFAULT_FOR_FIELD; } diff --git a/sql/rpl_record.h b/sql/rpl_record.h index 12c2f1fc713..0d6ceda7433 100644 --- a/sql/rpl_record.h +++ b/sql/rpl_record.h @@ -16,18 +16,22 @@ #ifndef RPL_RECORD_H #define RPL_RECORD_H +#include <rpl_reporting.h> + #if !defined(MYSQL_CLIENT) size_t pack_row(TABLE* table, MY_BITMAP const* cols, uchar *row_data, const uchar *data); #endif #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) -int unpack_row(RELAY_LOG_INFO const *rli, +int unpack_row(Relay_log_info const *rli, TABLE *table, uint const colcnt, uchar const *const row_data, MY_BITMAP const *cols, - uchar const **const row_end, ulong *const master_reclength, - MY_BITMAP* const rw_set, - Log_event_type const event_type); + uchar const **const row_end, ulong *const master_reclength); + +// Fill table's record[0] with default values. +int prepare_record(const Slave_reporting_capability *const, TABLE *const, + const uint =0, const bool =FALSE); #endif #endif diff --git a/sql/rpl_record_old.cc b/sql/rpl_record_old.cc index 377aec49113..ab4e993ce41 100644 --- a/sql/rpl_record_old.cc +++ b/sql/rpl_record_old.cc @@ -70,7 +70,7 @@ pack_row_old(TABLE *table, MY_BITMAP const* cols, */ #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) int -unpack_row_old(RELAY_LOG_INFO *rli, +unpack_row_old(Relay_log_info *rli, TABLE *table, uint const colcnt, uchar *record, uchar const *row, MY_BITMAP const *cols, uchar const **row_end, ulong *master_reclength, diff --git a/sql/rpl_record_old.h b/sql/rpl_record_old.h index 6af61141d47..bdaedd56741 100644 --- a/sql/rpl_record_old.h +++ b/sql/rpl_record_old.h @@ -21,7 +21,7 @@ size_t pack_row_old(TABLE *table, MY_BITMAP const* cols, uchar *row_data, const uchar *record); #ifdef HAVE_REPLICATION -int unpack_row_old(RELAY_LOG_INFO *rli, +int unpack_row_old(Relay_log_info *rli, TABLE *table, uint const colcnt, uchar *record, uchar const *row, MY_BITMAP const *cols, uchar const **row_end, ulong *master_reclength, diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 41df3f63825..867d55a60a3 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -21,7 +21,7 @@ #include "sql_repl.h" // For check_binlog_magic #include "rpl_utility.h" -static int count_relay_log_space(RELAY_LOG_INFO* rli); +static int count_relay_log_space(Relay_log_info* rli); // Defined in slave.cc int init_intvar_from_file(int* var, IO_CACHE* f, int default_val); @@ -29,7 +29,7 @@ int init_strvar_from_file(char *var, int max_size, IO_CACHE *f, const char *default_val); -st_relay_log_info::st_relay_log_info() +Relay_log_info::Relay_log_info() :Slave_reporting_capability("SQL"), no_storage(FALSE), replicate_same_server_id(::replicate_same_server_id), info_fd(-1), cur_log_fd(-1), save_temporary_tables(0), @@ -41,7 +41,7 @@ st_relay_log_info::st_relay_log_info() tables_to_lock(0), tables_to_lock_count(0), last_event_start_time(0), m_flags(0) { - DBUG_ENTER("st_relay_log_info::st_relay_log_info"); + DBUG_ENTER("Relay_log_info::Relay_log_info"); group_relay_log_name[0]= event_relay_log_name[0]= group_master_log_name[0]= 0; @@ -61,9 +61,9 @@ st_relay_log_info::st_relay_log_info() } -st_relay_log_info::~st_relay_log_info() +Relay_log_info::~Relay_log_info() { - DBUG_ENTER("st_relay_log_info::~st_relay_log_info"); + DBUG_ENTER("Relay_log_info::~Relay_log_info"); pthread_mutex_destroy(&run_lock); pthread_mutex_destroy(&data_lock); @@ -77,7 +77,7 @@ st_relay_log_info::~st_relay_log_info() } -int init_relay_log_info(RELAY_LOG_INFO* rli, +int init_relay_log_info(Relay_log_info* rli, const char* info_fname) { char fname[FN_REFLEN+128]; @@ -294,7 +294,7 @@ err: } -static inline int add_relay_log(RELAY_LOG_INFO* rli,LOG_INFO* linfo) +static inline int add_relay_log(Relay_log_info* rli,LOG_INFO* linfo) { MY_STAT s; DBUG_ENTER("add_relay_log"); @@ -313,7 +313,7 @@ static inline int add_relay_log(RELAY_LOG_INFO* rli,LOG_INFO* linfo) } -static int count_relay_log_space(RELAY_LOG_INFO* rli) +static int count_relay_log_space(Relay_log_info* rli) { LOG_INFO linfo; DBUG_ENTER("count_relay_log_space"); @@ -339,18 +339,18 @@ static int count_relay_log_space(RELAY_LOG_INFO* rli) /* - Reset UNTIL condition for RELAY_LOG_INFO + Reset UNTIL condition for Relay_log_info SYNOPSYS clear_until_condition() - rli - RELAY_LOG_INFO structure where UNTIL condition should be reset + rli - Relay_log_info structure where UNTIL condition should be reset */ -void st_relay_log_info::clear_until_condition() +void Relay_log_info::clear_until_condition() { DBUG_ENTER("clear_until_condition"); - until_condition= RELAY_LOG_INFO::UNTIL_NONE; + until_condition= Relay_log_info::UNTIL_NONE; until_log_name[0]= 0; until_log_pos= 0; DBUG_VOID_RETURN; @@ -389,7 +389,7 @@ void st_relay_log_info::clear_until_condition() 1 error. errmsg is set to point to the error message */ -int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, +int init_relay_log_pos(Relay_log_info* rli,const char* log, ulonglong pos, bool need_data_lock, const char** errmsg, bool look_for_description_event) @@ -599,7 +599,7 @@ err: before reaching the desired log/position */ -int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, +int Relay_log_info::wait_for_pos(THD* thd, String* log_name, longlong log_pos, longlong timeout) { @@ -608,7 +608,7 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, int error=0; struct timespec abstime; // for timeout checking const char *msg; - DBUG_ENTER("st_relay_log_info::wait_for_pos"); + DBUG_ENTER("Relay_log_info::wait_for_pos"); if (!inited) DBUG_RETURN(-1); @@ -779,10 +779,10 @@ improper_arguments: %d timed_out: %d", } -void st_relay_log_info::inc_group_relay_log_pos(ulonglong log_pos, +void Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos, bool skip_lock) { - DBUG_ENTER("st_relay_log_info::inc_group_relay_log_pos"); + DBUG_ENTER("Relay_log_info::inc_group_relay_log_pos"); if (!skip_lock) pthread_mutex_lock(&data_lock); @@ -836,10 +836,10 @@ void st_relay_log_info::inc_group_relay_log_pos(ulonglong log_pos, } -void st_relay_log_info::close_temporary_tables() +void Relay_log_info::close_temporary_tables() { TABLE *table,*next; - DBUG_ENTER("st_relay_log_info::close_temporary_tables"); + DBUG_ENTER("Relay_log_info::close_temporary_tables"); for (table=save_temporary_tables ; table ; table=next) { @@ -863,7 +863,7 @@ void st_relay_log_info::close_temporary_tables() Assumes to have a run lock on rli and that no slave thread are running. */ -int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset, +int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset, const char** errmsg) { int error=0; @@ -950,12 +950,12 @@ err: /* Check if condition stated in UNTIL clause of START SLAVE is reached. SYNOPSYS - st_relay_log_info::is_until_satisfied() + Relay_log_info::is_until_satisfied() DESCRIPTION Checks if UNTIL condition is reached. Uses caching result of last comparison of current log file name and target log file name. So cached value should be invalidated if current log file name changes - (see st_relay_log_info::notify_... functions). + (see Relay_log_info::notify_... functions). This caching is needed to avoid of expensive string comparisons and strtol() conversions needed for log names comparison. We don't need to @@ -975,11 +975,11 @@ err: false - condition not met */ -bool st_relay_log_info::is_until_satisfied() +bool Relay_log_info::is_until_satisfied() { const char *log_name; ulonglong log_pos; - DBUG_ENTER("st_relay_log_info::is_until_satisfied"); + DBUG_ENTER("Relay_log_info::is_until_satisfied"); DBUG_ASSERT(until_condition != UNTIL_NONE); @@ -1055,9 +1055,9 @@ bool st_relay_log_info::is_until_satisfied() } -void st_relay_log_info::cached_charset_invalidate() +void Relay_log_info::cached_charset_invalidate() { - DBUG_ENTER("st_relay_log_info::cached_charset_invalidate"); + DBUG_ENTER("Relay_log_info::cached_charset_invalidate"); /* Full of zeroes means uninitialized. */ bzero(cached_charset, sizeof(cached_charset)); @@ -1065,9 +1065,9 @@ void st_relay_log_info::cached_charset_invalidate() } -bool st_relay_log_info::cached_charset_compare(char *charset) const +bool Relay_log_info::cached_charset_compare(char *charset) const { - DBUG_ENTER("st_relay_log_info::cached_charset_compare"); + DBUG_ENTER("Relay_log_info::cached_charset_compare"); if (bcmp((uchar*) cached_charset, (uchar*) charset, sizeof(cached_charset))) @@ -1079,7 +1079,7 @@ bool st_relay_log_info::cached_charset_compare(char *charset) const } -void st_relay_log_info::stmt_done(my_off_t event_master_log_pos, +void Relay_log_info::stmt_done(my_off_t event_master_log_pos, time_t event_creation_time) { clear_flag(IN_STMT); @@ -1126,9 +1126,9 @@ void st_relay_log_info::stmt_done(my_off_t event_master_log_pos, } #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) -void st_relay_log_info::cleanup_context(THD *thd, bool error) +void Relay_log_info::cleanup_context(THD *thd, bool error) { - DBUG_ENTER("st_relay_log_info::cleanup_context"); + DBUG_ENTER("Relay_log_info::cleanup_context"); DBUG_ASSERT(sql_thd == thd); /* @@ -1156,7 +1156,7 @@ void st_relay_log_info::cleanup_context(THD *thd, bool error) DBUG_VOID_RETURN; } -void st_relay_log_info::clear_tables_to_lock() +void Relay_log_info::clear_tables_to_lock() { while (tables_to_lock) { diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index c458318594a..10ecf1a43d4 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -18,24 +18,25 @@ #include "rpl_tblmap.h" #include "rpl_reporting.h" +#include "rpl_utility.h" struct RPL_TABLE_LIST; - +class Master_info; /**************************************************************************** Replication SQL Thread - st_relay_log_info contains: + Relay_log_info contains: - the current relay log - the current relay log offset - master log name - master log sequence corresponding to the last update - misc information specific to the SQL thread - st_relay_log_info is initialized from the slave.info file if such exists. - Otherwise, data members are intialized with defaults. The initialization is - done with init_relay_log_info() call. + Relay_log_info is initialized from the slave.info file if such + exists. Otherwise, data members are intialized with defaults. The + initialization is done with init_relay_log_info() call. The format of slave.info file: @@ -48,8 +49,9 @@ struct RPL_TABLE_LIST; *****************************************************************************/ -typedef struct st_relay_log_info : public Slave_reporting_capability +class Relay_log_info : public Slave_reporting_capability { +public: /** Flags for the state of the replication. */ @@ -119,8 +121,8 @@ typedef struct st_relay_log_info : public Slave_reporting_capability */ pthread_cond_t start_cond, stop_cond, data_cond; - /* parent MASTER_INFO structure */ - class MASTER_INFO *mi; + /* parent Master_info structure */ + Master_info *mi; /* Needed to deal properly with cur_log getting closed and re-opened with @@ -254,8 +256,8 @@ typedef struct st_relay_log_info : public Slave_reporting_capability char ign_master_log_name_end[FN_REFLEN]; ulonglong ign_master_log_pos_end; - st_relay_log_info(); - ~st_relay_log_info(); + Relay_log_info(); + ~Relay_log_info(); /* Invalidate cached until_log_name and group_relay_log_name comparison @@ -301,6 +303,15 @@ typedef struct st_relay_log_info : public Slave_reporting_capability uint tables_to_lock_count; /* RBR: Count of tables to lock */ table_mapping m_table_map; /* RBR: Mapping table-id to table */ + inline table_def *get_tabledef(TABLE *tbl) + { + table_def *td= 0; + for (TABLE_LIST *ptr= tables_to_lock; ptr && !td; ptr= ptr->next_global) + if (ptr->table == tbl) + td= &((RPL_TABLE_LIST *)ptr)->m_tabledef; + return (td); + } + /* Last charset (6 bytes) seen by slave SQL thread is cached here; it helps the thread save 3 get_charset() per Query_log_event if the charset is not @@ -381,11 +392,11 @@ typedef struct st_relay_log_info : public Slave_reporting_capability private: uint32 m_flags; -} RELAY_LOG_INFO; +}; // Defined in rpl_rli.cc -int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname); +int init_relay_log_info(Relay_log_info* rli, const char* info_fname); #endif /* RPL_RLI_H */ diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc index a04bcd1fab9..d1ce5bf3b7b 100644 --- a/sql/rpl_utility.cc +++ b/sql/rpl_utility.cc @@ -16,17 +16,49 @@ #include "rpl_utility.h" #include "rpl_rli.h" -uint32 -field_length_from_packed(enum_field_types const field_type, - uchar const *const data) +/********************************************************************* + * table_def member definitions * + *********************************************************************/ + +/* + This function returns the field size in raw bytes based on the type + and the encoded field data from the master's raw data. +*/ +uint32 table_def::calc_field_size(uint col, uchar *master_data) const { uint32 length; - switch (field_type) { - case MYSQL_TYPE_DECIMAL: + switch (type(col)) { case MYSQL_TYPE_NEWDECIMAL: - length= ~(uint32) 0; + length= my_decimal_get_binary_size(m_field_metadata[col] >> 8, + m_field_metadata[col] - ((m_field_metadata[col] >> 8) << 8)); + break; + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + length= m_field_metadata[col]; + break; + case MYSQL_TYPE_SET: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_STRING: + { + if (((m_field_metadata[col] & 0xff00) == (MYSQL_TYPE_SET << 8)) || + ((m_field_metadata[col] & 0xff00) == (MYSQL_TYPE_ENUM << 8))) + length= m_field_metadata[col] & 0x00ff; + else + { + length= m_field_metadata[col] & 0x00ff; + DBUG_ASSERT(length > 0); + if (length > 255) + { + DBUG_ASSERT(uint2korr(master_data) > 0); + length= uint2korr(master_data) + 2; + } + else + length= (uint) *master_data + 1; + } break; + } case MYSQL_TYPE_YEAR: case MYSQL_TYPE_TINY: length= 1; @@ -45,12 +77,6 @@ field_length_from_packed(enum_field_types const field_type, length= 8; break; #endif - case MYSQL_TYPE_FLOAT: - length= sizeof(float); - break; - case MYSQL_TYPE_DOUBLE: - length= sizeof(double); - break; case MYSQL_TYPE_NULL: length= 0; break; @@ -58,8 +84,6 @@ field_length_from_packed(enum_field_types const field_type, length= 3; break; case MYSQL_TYPE_DATE: - length= 4; - break; case MYSQL_TYPE_TIME: length= 3; break; @@ -69,47 +93,78 @@ field_length_from_packed(enum_field_types const field_type, case MYSQL_TYPE_DATETIME: length= 8; break; - break; case MYSQL_TYPE_BIT: - length= ~(uint32) 0; - break; - default: - /* This case should never be chosen */ - DBUG_ASSERT(0); - /* If something goes awfully wrong, it's better to get a string than die */ - case MYSQL_TYPE_STRING: - length= uint2korr(data); + { + uint from_len= (m_field_metadata[col] >> 8U) & 0x00ff; + uint from_bit_len= m_field_metadata[col] & 0x00ff; + DBUG_ASSERT(from_bit_len <= 7); + length= from_len + ((from_bit_len > 0) ? 1 : 0); break; - - case MYSQL_TYPE_ENUM: - case MYSQL_TYPE_SET: - case MYSQL_TYPE_VAR_STRING: + } case MYSQL_TYPE_VARCHAR: - length= ~(uint32) 0; // NYI + { + length= m_field_metadata[col] > 255 ? 2 : 1; // c&p of Field_varstring::data_length() + DBUG_ASSERT(uint2korr(master_data) > 0); + length+= length == 1 ? (uint32) *master_data : uint2korr(master_data); break; - + } case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_GEOMETRY: - length= ~(uint32) 0; // NYI + { +#if 1 + /* + BUG#29549: + This is currently broken for NDB, which is using big-endian + order when packing length of BLOB. Once they have decided how to + fix the issue, we can enable the code below to make sure to + always read the length in little-endian order. + */ + Field_blob fb(m_field_metadata[col]); + length= fb.get_packed_size(master_data, TRUE); +#else + /* + Compute the length of the data. We cannot use get_length() here + since it is dependent on the specific table (and also checks the + packlength using the internal 'table' pointer) and replication + is using a fixed format for storing data in the binlog. + */ + switch (m_field_metadata[col]) { + case 1: + length= *master_data; + break; + case 2: + length= sint2korr(master_data); + break; + case 3: + length= uint3korr(master_data); + break; + case 4: + length= uint4korr(master_data); + break; + default: + DBUG_ASSERT(0); // Should not come here + break; + } + + length+= m_field_metadata[col]; +#endif break; } - + default: + length= -1; + } return length; } -/********************************************************************* - * table_def member definitions * - *********************************************************************/ - /* Is the definition compatible with a table? */ int -table_def::compatible_with(RELAY_LOG_INFO const *rli_arg, TABLE *table) +table_def::compatible_with(Relay_log_info const *rli_arg, TABLE *table) const { /* @@ -117,27 +172,10 @@ table_def::compatible_with(RELAY_LOG_INFO const *rli_arg, TABLE *table) */ uint const cols_to_check= min(table->s->fields, size()); int error= 0; - RELAY_LOG_INFO const *rli= const_cast<RELAY_LOG_INFO*>(rli_arg); + Relay_log_info const *rli= const_cast<Relay_log_info*>(rli_arg); TABLE_SHARE const *const tsh= table->s; - /* - To get proper error reporting for all columns of the table, we - both check the width and iterate over all columns. - */ - if (tsh->fields < size()) - { - DBUG_ASSERT(tsh->db.str && tsh->table_name.str); - error= 1; - char buf[256]; - my_snprintf(buf, sizeof(buf), "Table width mismatch - " - "received %u columns, %s.%s has %u columns", - (uint) size(), tsh->db.str, tsh->table_name.str, - tsh->fields); - rli->report(ERROR_LEVEL, ER_BINLOG_ROW_WRONG_TABLE_DEF, - ER(ER_BINLOG_ROW_WRONG_TABLE_DEF), buf); - } - for (uint col= 0 ; col < cols_to_check ; ++col) { if (table->field[col]->type() != type(col)) @@ -153,6 +191,25 @@ table_def::compatible_with(RELAY_LOG_INFO const *rli_arg, TABLE *table) rli->report(ERROR_LEVEL, ER_BINLOG_ROW_WRONG_TABLE_DEF, ER(ER_BINLOG_ROW_WRONG_TABLE_DEF), buf); } + /* + Check the slave's field size against that of the master. + */ + if (!error && + !table->field[col]->compatible_field_size(field_metadata(col))) + { + error= 1; + char buf[256]; + my_snprintf(buf, sizeof(buf), "Column %d size mismatch - " + "master has size %d, %s.%s on slave has size %d." + " Master's column size should be <= the slave's " + "column size.", col, + table->field[col]->pack_length_from_metadata( + m_field_metadata[col]), + tsh->db.str, tsh->table_name.str, + table->field[col]->row_pack_length()); + rli->report(ERROR_LEVEL, ER_BINLOG_ROW_WRONG_TABLE_DEF, + ER(ER_BINLOG_ROW_WRONG_TABLE_DEF), buf); + } } return error; diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h index 2ce8def4577..26edbdd1405 100644 --- a/sql/rpl_utility.h +++ b/sql/rpl_utility.h @@ -22,11 +22,8 @@ #include "mysql_priv.h" -struct st_relay_log_info; -typedef st_relay_log_info RELAY_LOG_INFO; +class Relay_log_info; -uint32 -field_length_from_packed(enum_field_types field_type, uchar const *data); /** A table definition from the master. @@ -57,19 +54,100 @@ public: @param types Array of types @param size Number of elements in array 'types' + @param field_metadata Array of extra information about fields + @param metadata_size Size of the field_metadata array + @param null_bitmap The bitmap of fields that can be null */ - table_def(field_type *types, ulong size) - : m_size(size), m_type(new unsigned char [size]) + table_def(field_type *types, ulong size, uchar *field_metadata, + int metadata_size, uchar *null_bitmap) + : m_size(size), m_type(0), m_field_metadata_size(metadata_size), + m_field_metadata(0), m_null_bits(0), m_memory(NULL) { + m_memory= (uchar *)my_multi_malloc(MYF(MY_WME), + &m_type, size, + &m_field_metadata, + size * sizeof(uint16), + &m_null_bits, (size + 7) / 8, + NULL); + + bzero(m_field_metadata, size * sizeof(uint16)); + if (m_type) memcpy(m_type, types, size); else m_size= 0; + /* + Extract the data from the table map into the field metadata array + iff there is field metadata. The variable metadata_size will be + 0 if we are replicating from an older version server since no field + metadata was written to the table map. This can also happen if + there were no fields in the master that needed extra metadata. + */ + if (m_size && metadata_size) + { + int index= 0; + for (unsigned int i= 0; i < m_size; i++) + { + switch (m_type[i]) { + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_FLOAT: + { + /* + These types store a single byte. + */ + m_field_metadata[i]= (uchar)field_metadata[index]; + index++; + break; + } + case MYSQL_TYPE_SET: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_STRING: + { + short int x= field_metadata[index++] << 8U; // real_type + x = x + field_metadata[index++]; // pack or field length + m_field_metadata[i]= x; + break; + } + case MYSQL_TYPE_BIT: + { + short int x= field_metadata[index++]; + x = x + (field_metadata[index++] << 8U); + m_field_metadata[i]= x; + break; + } + case MYSQL_TYPE_VARCHAR: + { + /* + These types store two bytes. + */ + char *ptr= (char *)&field_metadata[index]; + m_field_metadata[i]= sint2korr(ptr); + index= index + 2; + break; + } + case MYSQL_TYPE_NEWDECIMAL: + { + short int x= field_metadata[index++] << 8U; // precision + x = x + field_metadata[index++]; // decimals + m_field_metadata[i]= x; + break; + } + default: + m_field_metadata[i]= 0; + break; + } + } + } + if (m_size && null_bitmap) + memcpy(m_null_bits, null_bitmap, (m_size + 7) / 8); } ~table_def() { - if (m_type) - delete [] m_type; + my_free(m_memory, MYF(0)); #ifndef DBUG_OFF m_type= 0; m_size= 0; @@ -99,6 +177,48 @@ public: return m_type[index]; } + + /* + This function allows callers to get the extra field data from the + table map for a given field. If there is no metadata for that field + or there is no extra metadata at all, the function returns 0. + + The function returns the value for the field metadata for column at + position indicated by index. As mentioned, if the field was a type + that stores field metadata, that value is returned else zero (0) is + returned. This method is used in the unpack() methods of the + corresponding fields to properly extract the data from the binary log + in the event that the master's field is smaller than the slave. + */ + uint16 field_metadata(uint index) const + { + DBUG_ASSERT(index < m_size); + if (m_field_metadata_size) + return m_field_metadata[index]; + else + return 0; + } + + /* + This function returns whether the field on the master can be null. + This value is derived from field->maybe_null(). + */ + my_bool maybe_null(uint index) const + { + DBUG_ASSERT(index < m_size); + return ((m_null_bits[(index / 8)] & + (1 << (index % 8))) == (1 << (index %8))); + } + + /* + This function returns the field size in raw bytes based on the type + and the encoded field data from the master's raw data. This method can + be used for situations where the slave needs to skip a column (e.g., + WL#3915) or needs to advance the pointer for the fields in the raw + data from the master to a specific column. + */ + uint32 calc_field_size(uint col, uchar *master_data) const; + /** Decide if the table definition is compatible with a table. @@ -116,11 +236,15 @@ public: @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; + int compatible_with(Relay_log_info const *rli, TABLE *table) const; private: ulong m_size; // Number of elements in the types array field_type *m_type; // Array of type descriptors + uint m_field_metadata_size; + uint16 *m_field_metadata; + uchar *m_null_bits; + uchar *m_memory; }; /** @@ -128,10 +252,50 @@ private: slave thread, but nowhere else. */ struct RPL_TABLE_LIST - : public st_table_list + : public TABLE_LIST { bool m_tabledef_valid; table_def m_tabledef; }; + +/* Anonymous namespace for template functions/classes */ +namespace { + + /* + Smart pointer that will automatically call my_afree (a macro) when + the pointer goes out of scope. This is used so that I do not have + to remember to call my_afree() before each return. There is no + overhead associated with this, since all functions are inline. + + I (Matz) would prefer to use the free function as a template + parameter, but that is not possible when the "function" is a + macro. + */ + template <class Obj> + class auto_afree_ptr + { + Obj* m_ptr; + public: + auto_afree_ptr(Obj* ptr) : m_ptr(ptr) { } + ~auto_afree_ptr() { if (m_ptr) my_afree(m_ptr); } + void assign(Obj* ptr) { + /* Only to be called if it hasn't been given a value before. */ + DBUG_ASSERT(m_ptr == NULL); + m_ptr= ptr; + } + Obj* get() { return m_ptr; } + }; + +} + +#define DBUG_PRINT_BITSET(N,FRM,BS) \ + do { \ + char buf[256]; \ + for (uint i = 0 ; i < (BS)->n_bits ; ++i) \ + buf[i] = bitmap_is_set((BS), i) ? '1' : '0'; \ + buf[(BS)->n_bits] = '\0'; \ + DBUG_PRINT((N), ((FRM), buf)); \ + } while (0) + #endif /* RPL_UTILITY_H */ diff --git a/sql/set_var.cc b/sql/set_var.cc index ea47f2f557b..95bdc54a5e9 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -116,7 +116,6 @@ static void fix_max_join_size(THD *thd, enum_var_type type); static void fix_query_cache_size(THD *thd, enum_var_type type); static void fix_query_cache_min_res_unit(THD *thd, enum_var_type type); static void fix_myisam_max_sort_file_size(THD *thd, enum_var_type type); -static void fix_maria_max_sort_file_size(THD *thd, enum_var_type type); static void fix_max_binlog_size(THD *thd, enum_var_type type); static void fix_max_relay_log_size(THD *thd, enum_var_type type); static void fix_max_connections(THD *thd, enum_var_type type); @@ -258,15 +257,15 @@ static sys_var_bool_ptr sys_local_infile(&vars, "local_infile", static sys_var_trust_routine_creators sys_trust_routine_creators(&vars, "log_bin_trust_routine_creators", &trust_function_creators); -static sys_var_bool_ptr +static sys_var_bool_ptr sys_trust_function_creators(&vars, "log_bin_trust_function_creators", &trust_function_creators); static sys_var_bool_ptr sys_log_queries_not_using_indexes(&vars, "log_queries_not_using_indexes", &opt_log_queries_not_using_indexes); static sys_var_thd_ulong sys_log_warnings(&vars, "log_warnings", &SV::log_warnings); -static sys_var_thd_ulong sys_long_query_time(&vars, "long_query_time", - &SV::long_query_time); +static sys_var_microseconds sys_var_long_query_time(&vars, "long_query_time", + &SV::long_query_time); static sys_var_thd_bool sys_low_priority_updates(&vars, "low_priority_updates", &SV::low_priority_updates, fix_low_priority_updates); @@ -330,6 +329,8 @@ static sys_var_thd_ulong sys_max_tmp_tables(&vars, "max_tmp_tables", &SV::max_tmp_tables); static sys_var_long_ptr sys_max_write_lock_count(&vars, "max_write_lock_count", &max_write_lock_count); +static sys_var_thd_ulong sys_min_examined_row_limit(&vars, "min_examined_row_limit", + &SV::min_examined_row_limit); static sys_var_thd_ulong sys_multi_range_count(&vars, "multi_range_count", &SV::multi_range_count); static sys_var_long_ptr sys_myisam_data_pointer_size(&vars, "myisam_data_pointer_size", @@ -339,20 +340,10 @@ static sys_var_thd_ulong sys_myisam_repair_threads(&vars, "myisam_repair_t static sys_var_thd_ulong sys_myisam_sort_buffer_size(&vars, "myisam_sort_buffer_size", &SV::myisam_sort_buff_size); static sys_var_bool_ptr sys_myisam_use_mmap(&vars, "myisam_use_mmap", &opt_myisam_use_mmap); - static sys_var_thd_enum sys_myisam_stats_method(&vars, "myisam_stats_method", &SV::myisam_stats_method, &myisam_stats_method_typelib, NULL); - -static sys_var_thd_ulonglong sys_maria_max_sort_file_size(&vars, "maria_max_sort_file_size", &SV::maria_max_sort_file_size, fix_maria_max_sort_file_size, 1); -static sys_var_thd_ulong sys_maria_repair_threads(&vars, "maria_repair_threads", &SV::maria_repair_threads); -static sys_var_thd_ulong sys_maria_sort_buffer_size(&vars, "maria_sort_buffer_size", &SV::maria_sort_buff_size); -static sys_var_thd_enum sys_maria_stats_method(&vars, "maria_stats_method", - &SV::maria_stats_method, - &myisam_stats_method_typelib, - NULL); - static sys_var_thd_ulong sys_net_buffer_length(&vars, "net_buffer_length", &SV::net_buffer_length); static sys_var_thd_ulong sys_net_read_timeout(&vars, "net_read_timeout", @@ -639,6 +630,8 @@ sys_var_thd_time_zone sys_time_zone(&vars, "time_zone"); /* Global read-only variable containing hostname */ static sys_var_const_str sys_hostname(&vars, "hostname", glob_hostname); +sys_var_thd_bool sys_keep_files_on_create(&vars, "keep_files_on_create", + &SV::keep_files_on_create); /* Read only variables */ static sys_var_have_variable sys_have_compress(&vars, "have_compress", &have_compress); @@ -647,7 +640,7 @@ static sys_var_have_plugin sys_have_csv(&vars, "have_csv", C_STRING_WITH_LEN("cs static sys_var_have_variable sys_have_dlopen(&vars, "have_dynamic_loading", &have_dlopen); static sys_var_have_variable sys_have_geometry(&vars, "have_geometry", &have_geometry); static sys_var_have_plugin sys_have_innodb(&vars, "have_innodb", C_STRING_WITH_LEN("innodb"), MYSQL_STORAGE_ENGINE_PLUGIN); -static sys_var_have_variable sys_have_maria_db(&vars, "have_maria", &have_maria_db); +static sys_var_have_plugin sys_have_maria(&vars, "have_maria", C_STRING_WITH_LEN("maria"), MYSQL_STORAGE_ENGINE_PLUGIN); static sys_var_have_plugin sys_have_ndbcluster(&vars, "have_ndbcluster", C_STRING_WITH_LEN("ndbcluster"), MYSQL_STORAGE_ENGINE_PLUGIN); static sys_var_have_variable sys_have_openssl(&vars, "have_openssl", &have_ssl); static sys_var_have_variable sys_have_ssl(&vars, "have_ssl", &have_ssl); @@ -863,16 +856,6 @@ fix_myisam_max_sort_file_size(THD *thd, enum_var_type type) (my_off_t) global_system_variables.myisam_max_sort_file_size; } -static void -fix_maria_max_sort_file_size(THD *thd, enum_var_type type) -{ -#ifdef WITH_MARIA_STORAGE_ENGINE - maria_max_temp_length= - (my_off_t) global_system_variables.myisam_max_sort_file_size; -#endif -} - - /* Set the OPTION_BIG_SELECTS flag if max_join_size == HA_POS_ERROR */ @@ -1505,6 +1488,15 @@ Item *sys_var::item(THD *thd, enum_var_type var_type, LEX_STRING *base) pthread_mutex_unlock(&LOCK_global_system_variables); return new Item_int(value); } + case SHOW_DOUBLE: + { + double value; + pthread_mutex_lock(&LOCK_global_system_variables); + value= *(double*) value_ptr(thd, var_type, base); + pthread_mutex_unlock(&LOCK_global_system_variables); + /* 6, as this is for now only used with microseconds */ + return new Item_float(value, 6); + } case SHOW_HA_ROWS: { ha_rows value; @@ -2166,21 +2158,15 @@ end: bool sys_var_log_state::update(THD *thd, set_var *var) { - bool res= 0; + bool res; pthread_mutex_lock(&LOCK_global_system_variables); if (!var->save_result.ulong_value) - logger.deactivate_log_handler(thd, log_type); - else { - if ((res= logger.activate_log_handler(thd, log_type))) - { - my_error(ER_CANT_ACTIVATE_LOG, MYF(0), - log_type == QUERY_LOG_GENERAL ? "general" : - "slow query"); - goto err; - } + logger.deactivate_log_handler(thd, log_type); + res= false; } -err: + else + res= logger.activate_log_handler(thd, log_type); pthread_mutex_unlock(&LOCK_global_system_variables); return res; } @@ -2256,7 +2242,7 @@ bool update_sys_var_str_path(THD *thd, sys_var_str *var_str, } pthread_mutex_lock(&LOCK_global_system_variables); - logger.lock(); + logger.lock_exclusive(); if (file_log && log_state) file_log->close(0); @@ -2319,7 +2305,7 @@ static void sys_default_slow_log_path(THD *thd, enum_var_type type) bool sys_var_log_output::update(THD *thd, set_var *var) { pthread_mutex_lock(&LOCK_global_system_variables); - logger.lock(); + logger.lock_exclusive(); logger.init_slow_log(var->save_result.ulong_value); logger.init_general_log(var->save_result.ulong_value); *value= var->save_result.ulong_value; @@ -2332,10 +2318,10 @@ bool sys_var_log_output::update(THD *thd, set_var *var) void sys_var_log_output::set_default(THD *thd, enum_var_type type) { pthread_mutex_lock(&LOCK_global_system_variables); - logger.lock(); - logger.init_slow_log(LOG_TABLE); - logger.init_general_log(LOG_TABLE); - *value= LOG_TABLE; + logger.lock_exclusive(); + logger.init_slow_log(LOG_FILE); + logger.init_general_log(LOG_FILE); + *value= LOG_FILE; logger.unlock(); pthread_mutex_unlock(&LOCK_global_system_variables); } @@ -2646,6 +2632,60 @@ void sys_var_thd_lc_time_names::set_default(THD *thd, enum_var_type type) } /* + Handling of microseoncds given as seconds.part_seconds + + NOTES + The argument to long query time is in seconds in decimal + which is converted to ulonglong integer holding microseconds for storage. + This is used for handling long_query_time +*/ + +bool sys_var_microseconds::update(THD *thd, set_var *var) +{ + double num= var->value->val_real(); + longlong microseconds; + if (num > (double) option_limits->max_value) + num= (double) option_limits->max_value; + if (num < (double) option_limits->min_value) + num= (double) option_limits->min_value; + microseconds= (longlong) (num * 1000000.0 + 0.5); + if (var->type == OPT_GLOBAL) + { + pthread_mutex_lock(&LOCK_global_system_variables); + (global_system_variables.*offset)= microseconds; + pthread_mutex_unlock(&LOCK_global_system_variables); + } + else + thd->variables.*offset= microseconds; + return 0; +} + + +void sys_var_microseconds::set_default(THD *thd, enum_var_type type) +{ + longlong microseconds= (longlong) (option_limits->def_value * 1000000.0); + if (type == OPT_GLOBAL) + { + pthread_mutex_lock(&LOCK_global_system_variables); + global_system_variables.*offset= microseconds; + pthread_mutex_unlock(&LOCK_global_system_variables); + } + else + thd->variables.*offset= microseconds; +} + + +uchar *sys_var_microseconds::value_ptr(THD *thd, enum_var_type type, + LEX_STRING *base) +{ + thd->tmp_double_value= (double) ((type == OPT_GLOBAL) ? + global_system_variables.*offset : + thd->variables.*offset) / 1000000.0; + return (uchar*) &thd->tmp_double_value; +} + + +/* Functions to update thd->options bits */ @@ -2677,14 +2717,14 @@ static bool set_option_autocommit(THD *thd, set_var *var) { /* We changed to auto_commit mode */ thd->options&= ~(ulonglong) (OPTION_BEGIN | OPTION_KEEP_LOG); - thd->no_trans_update.all= FALSE; + thd->transaction.all.modified_non_trans_table= FALSE; thd->server_status|= SERVER_STATUS_AUTOCOMMIT; if (ha_commit(thd)) return 1; } else { - thd->no_trans_update.all= FALSE; + thd->transaction.all.modified_non_trans_table= FALSE; thd->server_status&= ~SERVER_STATUS_AUTOCOMMIT; } } @@ -3320,11 +3360,12 @@ bool sys_var_thd_storage_engine::check(THD *thd, set_var *var) var->save_result.plugin= NULL; if (var->value->result_type() == STRING_RESULT) { - LEX_STRING name; + LEX_STRING engine_name; handlerton *hton; if (!(res=var->value->val_str(&str)) || - !(name.str= (char *)res->ptr()) || !(name.length= res->length()) || - !(var->save_result.plugin= ha_resolve_by_name(thd, &name)) || + !(engine_name.str= (char *)res->ptr()) || + !(engine_name.length= res->length()) || + !(var->save_result.plugin= ha_resolve_by_name(thd, &engine_name)) || !(hton= plugin_data(var->save_result.plugin, handlerton *)) || ha_checktype(thd, ha_legacy_type(hton), 1, 0) != hton) { @@ -3346,13 +3387,13 @@ uchar *sys_var_thd_storage_engine::value_ptr(THD *thd, enum_var_type type, { uchar* result; handlerton *hton; - LEX_STRING *name; + LEX_STRING *engine_name; plugin_ref plugin= thd->variables.*offset; if (type == OPT_GLOBAL) plugin= my_plugin_lock(thd, &(global_system_variables.*offset)); hton= plugin_data(plugin, handlerton*); - name= &hton2plugin[hton->slot]->name; - result= (uchar *) thd->strmake(name->str, name->length); + engine_name= &hton2plugin[hton->slot]->name; + result= (uchar *) thd->strmake(engine_name->str, engine_name->length); if (type == OPT_GLOBAL) plugin_unlock(thd, plugin); return result; @@ -3639,7 +3680,7 @@ void free_key_cache(const char *name, KEY_CACHE *key_cache) } -bool process_key_caches(int (* func) (const char *name, KEY_CACHE *)) +bool process_key_caches(process_key_cache_t func) { I_List_iterator<NAMED_LIST> it(key_caches); NAMED_LIST *element; diff --git a/sql/set_var.h b/sql/set_var.h index 3c8c06b34df..b3ef4389038 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -113,9 +113,10 @@ class sys_var_long_ptr_global: public sys_var_global { public: ulong *value; - sys_var_long_ptr_global(sys_var_chain *chain, const char *name_arg, ulong *value_ptr_arg, - pthread_mutex_t *guard_arg, - sys_after_update_func after_update_arg= NULL) + sys_var_long_ptr_global(sys_var_chain *chain, 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_arg) { chain_sys_var(chain); } @@ -938,6 +939,27 @@ public: uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); }; + +class sys_var_microseconds :public sys_var_thd +{ + ulonglong SV::*offset; +public: + sys_var_microseconds(sys_var_chain *chain, const char *name_arg, + ulonglong SV::*offset_arg): + sys_var_thd(name_arg), offset(offset_arg) + { chain_sys_var(chain); } + bool check(THD *thd, set_var *var) {return 0;} + bool update(THD *thd, set_var *var); + void set_default(THD *thd, enum_var_type type); + SHOW_TYPE show_type() { return SHOW_DOUBLE; } + bool check_update_type(Item_result type) + { + return (type != INT_RESULT && type != REAL_RESULT && type != DECIMAL_RESULT); + } + uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); +}; + + class sys_var_trust_routine_creators :public sys_var_bool_ptr { /* We need a derived class only to have a warn_deprecated() */ @@ -1133,6 +1155,11 @@ public: }; +extern "C" +{ + typedef int (*process_key_cache_t) (const char *, KEY_CACHE *); +} + /* Named lists (used for keycaches) */ class NAMED_LIST :public ilink @@ -1157,8 +1184,7 @@ public: { my_free((uchar*) name, MYF(0)); } - friend bool process_key_caches(int (* func) (const char *name, - KEY_CACHE *)); + friend bool process_key_caches(process_key_cache_t func); #ifdef WITH_MARIA_STORAGE_ENGINE friend bool process_pagecaches(int (* func) (const char *name, PAGECACHE *)); @@ -1213,7 +1239,7 @@ extern sys_var_str sys_var_general_log_path, sys_var_slow_log_path; KEY_CACHE *get_key_cache(LEX_STRING *cache_name); KEY_CACHE *get_or_create_key_cache(const char *name, uint length); void free_key_cache(const char *name, KEY_CACHE *key_cache); -bool process_key_caches(int (* func) (const char *name, KEY_CACHE *)); +bool process_key_caches(process_key_cache_t func); void delete_elements(I_List<NAMED_LIST> *list, void (*free_element)(const char*, uchar*)); #ifdef WITH_MARIA_STORAGE_ENGINE diff --git a/sql/share/charsets/ascii.xml b/sql/share/charsets/ascii.xml index 97006c53680..f4fb79ac632 100644 --- a/sql/share/charsets/ascii.xml +++ b/sql/share/charsets/ascii.xml @@ -97,7 +97,7 @@ 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004A 004B 004C 004D 004E 004F 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005A 005B 005C 005D 005E 005F 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006A 006B 006C 006D 006E 006F -0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007A 007B 007C 007D 007E 0000 +0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007A 007B 007C 007D 007E 007F 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 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 @@ -117,9 +117,9 @@ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F - 50 51 52 53 54 55 56 57 58 59 5A 5C 5D 5B 5E 5F - 45 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F - 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 59 7F + 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F + 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F + 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index c899d7424e2..30cf85f3181 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5654,6 +5654,10 @@ ER_NON_INSERTABLE_TABLE ger "Die Zieltabelle %-.100s von %s ist nicht einfügbar" ER_ADMIN_WRONG_MRG_TABLE eng "Table '%-.64s' is differently defined or of non-MyISAM type or doesn't exist" +ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT + eng "Too high level of nesting for select" +ER_NAME_BECOMES_EMPTY + eng "Name '%-.64s' has become ''" ER_FOREIGN_SERVER_EXISTS eng "The foreign server, %s, you are trying to create already exists." ER_FOREIGN_SERVER_DOESNT_EXIST @@ -5936,8 +5940,8 @@ ER_WARN_DEPRECATED_SYNTAX_WITH_VER ER_CANT_WRITE_LOCK_LOG_TABLE eng "You can't write-lock a log table. Only read access is possible" ger "Eine Log-Tabelle kann nicht schreibgesperrt werden. Es ist ohnehin nur Lesezugriff möglich" -ER_CANT_READ_LOCK_LOG_TABLE - eng "You can't use usual read lock with log tables. Try READ LOCAL instead" +ER_CANT_LOCK_LOG_TABLE + eng "You can't use locks with log tables." ger "Log-Tabellen können nicht mit normalen Lesesperren gesperrt werden. Verwenden Sie statt dessen READ LOCAL" ER_FOREIGN_DUPLICATE_KEY 23000 S1009 eng "Upholding foreign key constraints for table '%.192s', entry '%-.192s', key %d would lead to a duplicate entry" @@ -6062,9 +6066,9 @@ 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" ER_EVENT_CANNOT_CREATE_IN_THE_PAST - eng "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. Event has not been created" + eng "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation." ER_EVENT_CANNOT_ALTER_IN_THE_PAST - eng "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. Event has not been altered" + eng "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation." ER_SLAVE_INCIDENT eng "The incident %s occured on the master. Message: %-.64s" ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT diff --git a/sql/slave.cc b/sql/slave.cc index 512c4bc8d12..fcbd4eb841b 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -27,15 +27,14 @@ #include <my_dir.h> #include <sql_common.h> #include <errmsg.h> - -static Log_event* next_event(RELAY_LOG_INFO* rli); +#include <mysys_err.h> #ifdef HAVE_REPLICATION #include "rpl_tblmap.h" -int queue_event(MASTER_INFO* mi,const char* buf,ulong event_len); -static Log_event* next_event(RELAY_LOG_INFO* rli); +int queue_event(Master_info* mi,const char* buf,ulong event_len); +static Log_event* next_event(Relay_log_info* rli); #define FLAGSTR(V,F) ((V)&(F)?#F" ":"") @@ -47,7 +46,7 @@ MY_BITMAP slave_error_mask; typedef bool (*CHECK_KILLED_FUNC)(THD*,void*); char* slave_load_tmpdir = 0; -MASTER_INFO *active_mi= 0; +Master_info *active_mi= 0; my_bool replicate_same_server_id; ulonglong relay_log_space_limit = 0; @@ -55,32 +54,89 @@ ulonglong relay_log_space_limit = 0; When slave thread exits, we need to remember the temporary tables so we can re-use them on slave start. - TODO: move the vars below under MASTER_INFO + TODO: move the vars below under Master_info */ int disconnect_slave_event_count = 0, abort_slave_event_count = 0; int events_till_abort = -1; +enum enum_slave_reconnect_actions +{ + SLAVE_RECON_ACT_REG= 0, + SLAVE_RECON_ACT_DUMP= 1, + SLAVE_RECON_ACT_EVENT= 2, + SLAVE_RECON_ACT_MAX +}; + +enum enum_slave_reconnect_messages +{ + SLAVE_RECON_MSG_WAIT= 0, + SLAVE_RECON_MSG_KILLED_WAITING= 1, + SLAVE_RECON_MSG_AFTER= 2, + SLAVE_RECON_MSG_FAILED= 3, + SLAVE_RECON_MSG_COMMAND= 4, + SLAVE_RECON_MSG_KILLED_AFTER= 5, + SLAVE_RECON_MSG_MAX +}; + +static const char *reconnect_messages[SLAVE_RECON_ACT_MAX][SLAVE_RECON_MSG_MAX]= +{ + { + "Waiting to reconnect after a failed registration on master", + "Slave I/O thread killed while waitnig to reconnect after a failed \ +registration on master", + "Reconnecting after a failed registration on master", + "failed registering on master, reconnecting to try again, \ +log '%s' at postion %s", + "COM_REGISTER_SLAVE", + "Slave I/O thread killed during or after reconnect" + }, + { + "Waiting to reconnect after a failed binlog dump request", + "Slave I/O thread killed while retrying master dump", + "Reconnecting after a failed binlog dump request", + "failed dump request, reconnecting to try again, log '%s' at postion %s", + "COM_BINLOG_DUMP", + "Slave I/O thread killed during or after reconnect" + }, + { + "Waiting to reconnect after a failed master event read", + "Slave I/O thread killed while waiting to reconnect after a failed read", + "Reconnecting after a failed master event read", + "Slave I/O thread: Failed reading log event, reconnecting to retry, \ +log '%s' at postion %s", + "", + "Slave I/O thread killed during or after a reconnect done to recover from \ +failed read" + } +}; + + typedef enum { SLAVE_THD_IO, SLAVE_THD_SQL} SLAVE_THD_TYPE; -static int process_io_rotate(MASTER_INFO* mi, Rotate_log_event* rev); -static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev); -static bool wait_for_relay_log_space(RELAY_LOG_INFO* rli); -static inline bool io_slave_killed(THD* thd,MASTER_INFO* mi); -static inline bool sql_slave_killed(THD* thd,RELAY_LOG_INFO* rli); +static int process_io_rotate(Master_info* mi, Rotate_log_event* rev); +static int process_io_create_file(Master_info* mi, Create_file_log_event* cev); +static bool wait_for_relay_log_space(Relay_log_info* rli); +static inline bool io_slave_killed(THD* thd,Master_info* mi); +static inline bool sql_slave_killed(THD* thd,Relay_log_info* rli); static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type); -static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi); -static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi, +static int safe_connect(THD* thd, MYSQL* mysql, Master_info* mi); +static int safe_reconnect(THD* thd, MYSQL* mysql, Master_info* mi, bool suppress_warnings); -static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi, +static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi, bool reconnect, bool suppress_warnings); static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed, void* thread_killed_arg); 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); +static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi); +static Log_event* next_event(Relay_log_info* rli); +static int terminate_slave_thread(THD *thd, + pthread_mutex_t* term_lock, + pthread_cond_t* term_cond, + volatile uint *slave_running, + bool skip_lock); /* Find out which replications threads are running @@ -100,7 +156,7 @@ static Log_event* next_event(RELAY_LOG_INFO* rli); If inverse == 1, stopped threads */ -void init_thread_mask(int* mask,MASTER_INFO* mi,bool inverse) +void init_thread_mask(int* mask,Master_info* mi,bool inverse) { bool set_io = mi->slave_running, set_sql = mi->rli.slave_running; register int tmp_mask=0; @@ -121,7 +177,7 @@ void init_thread_mask(int* mask,MASTER_INFO* mi,bool inverse) lock_slave_threads() */ -void lock_slave_threads(MASTER_INFO* mi) +void lock_slave_threads(Master_info* mi) { DBUG_ENTER("lock_slave_threads"); @@ -136,7 +192,7 @@ void lock_slave_threads(MASTER_INFO* mi) unlock_slave_threads() */ -void unlock_slave_threads(MASTER_INFO* mi) +void unlock_slave_threads(Master_info* mi) { DBUG_ENTER("unlock_slave_threads"); @@ -163,7 +219,7 @@ int init_slave() TODO: re-write this to interate through the list of files for multi-master */ - active_mi= new MASTER_INFO; + active_mi= new Master_info; /* If master_host is not specified, try to read it from the master_info file. @@ -253,7 +309,7 @@ void init_slave_skip_errors(const char* arg) } -int terminate_slave_threads(MASTER_INFO* mi,int thread_mask,bool skip_lock) +int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock) { DBUG_ENTER("terminate_slave_threads"); @@ -261,35 +317,26 @@ int terminate_slave_threads(MASTER_INFO* mi,int thread_mask,bool skip_lock) DBUG_RETURN(0); /* successfully do nothing */ int error,force_all = (thread_mask & SLAVE_FORCE_ALL); pthread_mutex_t *sql_lock = &mi->rli.run_lock, *io_lock = &mi->run_lock; - pthread_mutex_t *sql_cond_lock,*io_cond_lock; - - sql_cond_lock=sql_lock; - io_cond_lock=io_lock; - if (skip_lock) - { - sql_lock = io_lock = 0; - } - if ((thread_mask & (SLAVE_IO|SLAVE_FORCE_ALL)) && mi->slave_running) + if ((thread_mask & (SLAVE_IO|SLAVE_FORCE_ALL))) { DBUG_PRINT("info",("Terminating IO thread")); mi->abort_slave=1; if ((error=terminate_slave_thread(mi->io_thd,io_lock, - io_cond_lock, &mi->stop_cond, - &mi->slave_running)) && + &mi->slave_running, + skip_lock)) && !force_all) DBUG_RETURN(error); } - if ((thread_mask & (SLAVE_SQL|SLAVE_FORCE_ALL)) && mi->rli.slave_running) + if ((thread_mask & (SLAVE_SQL|SLAVE_FORCE_ALL))) { DBUG_PRINT("info",("Terminating SQL thread")); - DBUG_ASSERT(mi->rli.sql_thd != 0) ; mi->rli.abort_slave=1; if ((error=terminate_slave_thread(mi->rli.sql_thd,sql_lock, - sql_cond_lock, &mi->rli.stop_cond, - &mi->rli.slave_running)) && + &mi->rli.slave_running, + skip_lock)) && !force_all) DBUG_RETURN(error); } @@ -297,23 +344,60 @@ int terminate_slave_threads(MASTER_INFO* mi,int thread_mask,bool skip_lock) } -int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock, - pthread_mutex_t *cond_lock, - pthread_cond_t* term_cond, - volatile uint *slave_running) +/** + Wait for a slave thread to terminate. + + This function is called after requesting the thread to terminate + (by setting @c abort_slave member of @c Relay_log_info or @c + Master_info structure to 1). Termination of the thread is + controlled with the the predicate <code>*slave_running</code>. + + Function will acquire @c term_lock before waiting on the condition + unless @c skip_lock is true in which case the mutex should be owned + by the caller of this function and will remain acquired after + return from the function. + + @param term_lock + Associated lock to use when waiting for @c term_cond + + @param term_cond + Condition that is signalled when the thread has terminated + + @param slave_running + Pointer to predicate to check for slave thread termination + + @param skip_lock + If @c true the lock will not be acquired before waiting on + the condition. In this case, it is assumed that the calling + function acquires the lock before calling this function. + + @retval 0 All OK + */ +static int +terminate_slave_thread(THD *thd, + pthread_mutex_t* term_lock, + pthread_cond_t* term_cond, + volatile uint *slave_running, + bool skip_lock) { + int error; + DBUG_ENTER("terminate_slave_thread"); - if (term_lock) - { + + if (!skip_lock) pthread_mutex_lock(term_lock); - if (!*slave_running) - { + + safe_mutex_assert_owner(term_lock); + + if (!*slave_running) + { + if (!skip_lock) pthread_mutex_unlock(term_lock); - DBUG_RETURN(ER_SLAVE_NOT_RUNNING); - } + DBUG_RETURN(ER_SLAVE_NOT_RUNNING); } DBUG_ASSERT(thd != 0); THD_CHECK_SENTRY(thd); + /* Is is critical to test if the slave is running. Otherwise, we might be referening freed memory trying to kick it @@ -329,9 +413,13 @@ int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock, */ struct timespec abstime; set_timespec(abstime,2); - pthread_cond_timedwait(term_cond, cond_lock, &abstime); + error= pthread_cond_timedwait(term_cond, term_lock, &abstime); + DBUG_ASSERT(error == ETIMEDOUT || error == 0); } - if (term_lock) + + DBUG_ASSERT(*slave_running == 0); + + if (!skip_lock) pthread_mutex_unlock(term_lock); DBUG_RETURN(0); } @@ -342,7 +430,7 @@ int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock, pthread_cond_t *start_cond, volatile uint *slave_running, volatile ulong *slave_run_id, - MASTER_INFO* mi, + Master_info* mi, bool high_priority) { pthread_t th; @@ -412,7 +500,7 @@ int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock, */ int start_slave_threads(bool need_slave_mutex, bool wait_for_start, - MASTER_INFO* mi, const char* master_info_fname, + Master_info* mi, const char* master_info_fname, const char* slave_info_fname, int thread_mask) { pthread_mutex_t *lock_io=0,*lock_sql=0,*lock_cond_io=0,*lock_cond_sql=0; @@ -452,7 +540,7 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start, #ifdef NOT_USED_YET -static int end_slave_on_walk(MASTER_INFO* mi, uchar* /*unused*/) +static int end_slave_on_walk(Master_info* mi, uchar* /*unused*/) { DBUG_ENTER("end_slave_on_walk"); @@ -498,7 +586,7 @@ void end_slave() } -static bool io_slave_killed(THD* thd, MASTER_INFO* mi) +static bool io_slave_killed(THD* thd, Master_info* mi) { DBUG_ENTER("io_slave_killed"); @@ -508,7 +596,7 @@ static bool io_slave_killed(THD* thd, MASTER_INFO* mi) } -static bool sql_slave_killed(THD* thd, RELAY_LOG_INFO* rli) +static bool sql_slave_killed(THD* thd, Relay_log_info* rli) { DBUG_ENTER("sql_slave_killed"); @@ -648,7 +736,7 @@ int init_intvar_from_file(int* var, IO_CACHE* f, int default_val) 1 error */ -static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi) +static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi) { const char* errmsg= 0; DBUG_ENTER("get_master_version_and_clock"); @@ -926,7 +1014,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, thd->proc_info = "Opening master dump table"; tables.lock_type = TL_WRITE; - if (!open_ltable(thd, &tables, TL_WRITE)) + if (!open_ltable(thd, &tables, TL_WRITE, 0)) { sql_print_error("create_table_from_dump: could not open created table"); goto err; @@ -967,7 +1055,7 @@ err: int fetch_master_table(THD *thd, const char *db_name, const char *table_name, - MASTER_INFO *mi, MYSQL *mysql, bool overwrite) + Master_info *mi, MYSQL *mysql, bool overwrite) { int error= 1; const char *errmsg=0; @@ -1022,10 +1110,10 @@ int fetch_master_table(THD *thd, const char *db_name, const char *table_name, } -static bool wait_for_relay_log_space(RELAY_LOG_INFO* rli) +static bool wait_for_relay_log_space(Relay_log_info* rli) { bool slave_killed=0; - MASTER_INFO* mi = rli->mi; + Master_info* mi = rli->mi; const char *save_proc_info; THD* thd = mi->io_thd; DBUG_ENTER("wait_for_relay_log_space"); @@ -1057,9 +1145,9 @@ Waiting for the slave SQL thread to free enough relay log space"); ignored events' end position for the use of the slave SQL thread, by calling this function. Only that thread can call it (see assertion). */ -static void write_ignored_events_info_to_relay_log(THD *thd, MASTER_INFO *mi) +static void write_ignored_events_info_to_relay_log(THD *thd, Master_info *mi) { - RELAY_LOG_INFO *rli= &mi->rli; + Relay_log_info *rli= &mi->rli; pthread_mutex_t *log_lock= rli->relay_log.get_log_lock(); DBUG_ENTER("write_ignored_events_info_to_relay_log"); @@ -1100,12 +1188,14 @@ static void write_ignored_events_info_to_relay_log(THD *thd, MASTER_INFO *mi) } -int register_slave_on_master(MYSQL* mysql, MASTER_INFO *mi) +int register_slave_on_master(MYSQL* mysql, Master_info *mi, + bool *suppress_warnings) { uchar buf[1024], *pos= buf; uint report_host_len, report_user_len=0, report_password_len=0; DBUG_ENTER("register_slave_on_master"); + *suppress_warnings= FALSE; if (!report_host) DBUG_RETURN(0); report_host_len= strlen(report_host); @@ -1129,18 +1219,25 @@ int register_slave_on_master(MYSQL* mysql, MASTER_INFO *mi) if (simple_command(mysql, COM_REGISTER_SLAVE, buf, (size_t) (pos- buf), 0)) { - char buf[256]; - my_snprintf(buf, sizeof(buf), - "%s (Errno: %d)", mysql_error(mysql), mysql_errno(mysql)); - mi->report(ERROR_LEVEL, ER_SLAVE_MASTER_COM_FAILURE, - ER(ER_SLAVE_MASTER_COM_FAILURE), "COM_REGISTER_SLAVE", buf); + if (mysql_errno(mysql) == ER_NET_READ_INTERRUPTED) + { + *suppress_warnings= TRUE; // Suppress reconnect warning + } + else + { + char buf[256]; + my_snprintf(buf, sizeof(buf), "%s (Errno: %d)", mysql_error(mysql), + mysql_errno(mysql)); + mi->report(ERROR_LEVEL, ER_SLAVE_MASTER_COM_FAILURE, + ER(ER_SLAVE_MASTER_COM_FAILURE), "COM_REGISTER_SLAVE", buf); + } DBUG_RETURN(1); } DBUG_RETURN(0); } -bool show_master_info(THD* thd, MASTER_INFO* mi) +bool show_master_info(THD* thd, Master_info* mi) { // TODO: fix this for multi-master List<Item> field_list; @@ -1262,8 +1359,8 @@ bool show_master_info(THD* thd, MASTER_INFO* mi) protocol->store((ulonglong) mi->rli.log_space_total); protocol->store( - mi->rli.until_condition==RELAY_LOG_INFO::UNTIL_NONE ? "None": - ( mi->rli.until_condition==RELAY_LOG_INFO::UNTIL_MASTER_POS? "Master": + mi->rli.until_condition==Relay_log_info::UNTIL_NONE ? "None": + ( mi->rli.until_condition==Relay_log_info::UNTIL_MASTER_POS? "Master": "Relay"), &my_charset_bin); protocol->store(mi->rli.until_log_name, &my_charset_bin); protocol->store((ulonglong) mi->rli.until_log_pos); @@ -1359,7 +1456,7 @@ void set_slave_thread_options(THD* thd) DBUG_VOID_RETURN; } -void set_slave_thread_default_charset(THD* thd, RELAY_LOG_INFO const *rli) +void set_slave_thread_default_charset(THD* thd, Relay_log_info const *rli) { DBUG_ENTER("set_slave_thread_default_charset"); @@ -1377,7 +1474,7 @@ void set_slave_thread_default_charset(THD* thd, RELAY_LOG_INFO const *rli) the thread. That the cache has to be invalidated is a secondary effect. */ - const_cast<RELAY_LOG_INFO*>(rli)->cached_charset_invalidate(); + const_cast<Relay_log_info*>(rli)->cached_charset_invalidate(); DBUG_VOID_RETURN; } @@ -1400,6 +1497,7 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type) 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; + thd->enable_slow_log= opt_log_slow_slave_statements; set_slave_thread_options(thd); thd->client_capabilities = CLIENT_LOCAL_FILES; pthread_mutex_lock(&LOCK_thread_count); @@ -1431,7 +1529,7 @@ static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed, DBUG_ENTER("safe_sleep"); thr_alarm_init(&alarmed); - time_t start_time= time((time_t*) 0); + time_t start_time= my_time(0); time_t end_time= start_time+sec; while ((nap_time= (int) (end_time - start_time)) > 0) @@ -1448,13 +1546,13 @@ static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed, if ((*thread_killed)(thd,thread_killed_arg)) DBUG_RETURN(1); - start_time=time((time_t*) 0); + start_time= my_time(0); } DBUG_RETURN(0); } -static int request_dump(MYSQL* mysql, MASTER_INFO* mi, +static int request_dump(MYSQL* mysql, Master_info* mi, bool *suppress_warnings) { uchar buf[FN_REFLEN + 10]; @@ -1462,6 +1560,8 @@ static int request_dump(MYSQL* mysql, MASTER_INFO* mi, int binlog_flags = 0; // for now char* logname = mi->master_log_name; DBUG_ENTER("request_dump"); + + *suppress_warnings= FALSE; // TODO if big log files: Change next to int8store() int4store(buf, (ulong) mi->master_log_pos); @@ -1477,7 +1577,7 @@ static int request_dump(MYSQL* mysql, MASTER_INFO* mi, now we just fill up the error log :-) */ if (mysql_errno(mysql) == ER_NET_READ_INTERRUPTED) - *suppress_warnings= 1; // Suppress reconnect warning + *suppress_warnings= TRUE; // Suppress reconnect warning else sql_print_error("Error on COM_BINLOG_DUMP: %d %s, will retry in %d secs", mysql_errno(mysql), mysql_error(mysql), @@ -1536,12 +1636,12 @@ command"); number Length of packet */ -static ulong read_event(MYSQL* mysql, MASTER_INFO *mi, bool* suppress_warnings) +static ulong read_event(MYSQL* mysql, Master_info *mi, bool* suppress_warnings) { ulong len; DBUG_ENTER("read_event"); - *suppress_warnings= 0; + *suppress_warnings= FALSE; /* my_real_read() will time us out We check if we were told to die, and if not, try reading again @@ -1584,7 +1684,7 @@ static ulong read_event(MYSQL* mysql, MASTER_INFO *mi, bool* suppress_warnings) } -int check_expected_error(THD* thd, RELAY_LOG_INFO const *rli, +int check_expected_error(THD* thd, Relay_log_info const *rli, int expected_error) { DBUG_ENTER("check_expected_error"); @@ -1646,7 +1746,7 @@ static int has_temporary_error(THD *thd) DBUG_RETURN(0); } -static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) +static int exec_relay_log_event(THD* thd, Relay_log_info* rli) { DBUG_ENTER("exec_relay_log_event"); @@ -1664,7 +1764,7 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) was an event ignored by the I/O thread just before (BUG#13861 to be fixed). */ - if (rli->until_condition!=RELAY_LOG_INFO::UNTIL_NONE && + if (rli->until_condition!=Relay_log_info::UNTIL_NONE && rli->is_until_satisfied()) { char buf[22]; @@ -1734,7 +1834,7 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) thd->set_time(); // time the query thd->lex->current_select= 0; if (!ev->when) - ev->when = time(NULL); + ev->when= my_time(0); ev->thd = thd; // because up to this point, ev->thd == 0 int reason= ev->shall_skip(rli); @@ -1881,17 +1981,108 @@ on this slave.\ } +static bool check_io_slave_killed(THD *thd, Master_info *mi, const char *info) +{ + if (io_slave_killed(thd, mi)) + { + if (global_system_variables.log_warnings) + sql_print_information(info); + return TRUE; + } + return FALSE; +} + + +/** + @brief Try to reconnect slave IO thread. + + @details Terminates current connection to master, sleeps for + @c mi->connect_retry msecs and initiates new connection with + @c safe_reconnect(). Variable pointed by @c retry_count is increased - + if it exceeds @c master_retry_count then connection is not re-established + and function signals error. + Unless @c suppres_warnings is TRUE, a warning is put in the server error log + when reconnecting. The warning message and messages used to report errors + are taken from @c messages array. In case @c master_retry_count is exceeded, + no messages are added to the log. + + @param[in] thd Thread context. + @param[in] mysql MySQL connection. + @param[in] mi Master connection information. + @param[in,out] retry_count Number of attempts to reconnect. + @param[in] suppress_warnings TRUE when a normal net read timeout + has caused to reconnecting. + @param[in] messages Messages to print/log, see + reconnect_messages[] array. + + @retval 0 OK. + @retval 1 There was an error. +*/ + +static int try_to_reconnect(THD *thd, MYSQL *mysql, Master_info *mi, + uint *retry_count, bool suppress_warnings, + const char *messages[SLAVE_RECON_MSG_MAX]) +{ + mi->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT; + thd->proc_info= messages[SLAVE_RECON_MSG_WAIT]; +#ifdef SIGNAL_WITH_VIO_CLOSE + thd->clear_active_vio(); +#endif + end_server(mysql); + if ((*retry_count)++) + { + if (*retry_count > master_retry_count) + return 1; // Don't retry forever + safe_sleep(thd, mi->connect_retry, (CHECK_KILLED_FUNC) io_slave_killed, + (void *) mi); + } + if (check_io_slave_killed(thd, mi, messages[SLAVE_RECON_MSG_KILLED_WAITING])) + return 1; + thd->proc_info = messages[SLAVE_RECON_MSG_AFTER]; + if (!suppress_warnings) + { + char buf[256], llbuff[22]; + my_snprintf(buf, sizeof(buf), messages[SLAVE_RECON_MSG_FAILED], + IO_RPL_LOG_NAME, llstr(mi->master_log_pos, llbuff)); + /* + Raise a warining during registering on master/requesting dump. + Log a message reading event. + */ + if (messages[SLAVE_RECON_MSG_COMMAND][0]) + { + mi->report(WARNING_LEVEL, ER_SLAVE_MASTER_COM_FAILURE, + ER(ER_SLAVE_MASTER_COM_FAILURE), + messages[SLAVE_RECON_MSG_COMMAND], buf); + } + else + { + sql_print_information(buf); + } + } + if (safe_reconnect(thd, mysql, mi, 1) || io_slave_killed(thd, mi)) + { + if (global_system_variables.log_warnings) + sql_print_information(messages[SLAVE_RECON_MSG_KILLED_AFTER]); + return 1; + } + return 0; +} + + /* Slave I/O Thread entry point */ pthread_handler_t handle_slave_io(void *arg) { THD *thd; // needs to be first for thread_stack MYSQL *mysql; - MASTER_INFO *mi = (MASTER_INFO*)arg; - RELAY_LOG_INFO *rli= &mi->rli; + Master_info *mi = (Master_info*)arg; + Relay_log_info *rli= &mi->rli; char llbuff[22]; uint retry_count; - + bool suppress_warnings; +#ifndef DBUG_OFF + uint retry_count_reg= 0, retry_count_dump= 0, retry_count_event= 0; +#endif // needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff my_thread_init(); DBUG_ENTER("handle_slave_io"); @@ -1977,79 +2168,56 @@ connected: Register ourselves with the master. */ thd->proc_info = "Registering slave on master"; - if (register_slave_on_master(mysql, mi)) + if (register_slave_on_master(mysql, mi, &suppress_warnings)) { sql_print_error("Slave I/O thread couldn't register on master"); - goto err; + if (check_io_slave_killed(thd, mi, "Slave I/O thread killed while \ +registering slave on master") || + try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings, + reconnect_messages[SLAVE_RECON_ACT_REG])) + goto err; + goto connected; } + DBUG_EXECUTE_IF("FORCE_SLAVE_TO_RECONNECT_REG", + if (!retry_count_reg) + { + retry_count_reg++; + sql_print_information("Forcing to reconnect slave I/O thread"); + if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings, + reconnect_messages[SLAVE_RECON_ACT_REG])) + goto err; + goto connected; + }); } DBUG_PRINT("info",("Starting reading binary log from master")); while (!io_slave_killed(thd,mi)) { - bool suppress_warnings= 0; thd->proc_info = "Requesting binlog dump"; if (request_dump(mysql, mi, &suppress_warnings)) { sql_print_error("Failed on request_dump()"); - if (io_slave_killed(thd,mi)) - { - sql_print_information("Slave I/O thread killed while requesting master \ -dump"); - goto err; - } - - mi->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT; - thd->proc_info= "Waiting to reconnect after a failed binlog dump request"; -#ifdef SIGNAL_WITH_VIO_CLOSE - thd->clear_active_vio(); -#endif - end_server(mysql); - /* - First time retry immediately, assuming that we can recover - right away - if first time fails, sleep between re-tries - hopefuly the admin can fix the problem sometime - */ - if (retry_count++) - { - if (retry_count > master_retry_count) - goto err; // Don't retry forever - safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed, - (void*)mi); - } - if (io_slave_killed(thd,mi)) - { - sql_print_information("Slave I/O thread killed while retrying master \ -dump"); + if (check_io_slave_killed(thd, mi, "Slave I/O thread killed while \ +requesting master dump") || + try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings, + reconnect_messages[SLAVE_RECON_ACT_DUMP])) goto err; - } - - thd->proc_info = "Reconnecting after a failed binlog dump request"; - if (!suppress_warnings) { - char buf[256]; - my_snprintf(buf, sizeof(buf), - "failed dump request, reconnecting to try again," - " log '%s' at postion %s", - IO_RPL_LOG_NAME, - llstr(mi->master_log_pos,llbuff)); - mi->report(WARNING_LEVEL, ER_SLAVE_MASTER_COM_FAILURE, - ER(ER_SLAVE_MASTER_COM_FAILURE), "COM_BINLOG_DUMP", buf); - } - if (safe_reconnect(thd, mysql, mi, suppress_warnings) || - io_slave_killed(thd,mi)) - { - sql_print_information("Slave I/O thread killed during or \ -after reconnect"); - goto err; - } - goto connected; } + DBUG_EXECUTE_IF("FORCE_SLAVE_TO_RECONNECT_DUMP", + if (!retry_count_dump) + { + retry_count_dump++; + sql_print_information("Forcing to reconnect slave I/O thread"); + if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings, + reconnect_messages[SLAVE_RECON_ACT_DUMP])) + goto err; + goto connected; + }); while (!io_slave_killed(thd,mi)) { 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 @@ -2058,64 +2226,44 @@ after reconnect"); */ 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) - sql_print_information("Slave I/O thread killed while reading event"); + if (check_io_slave_killed(thd, mi, "Slave I/O thread killed while \ +reading event")) goto err; - } + DBUG_EXECUTE_IF("FORCE_SLAVE_TO_RECONNECT_EVENT", + if (!retry_count_event) + { + retry_count_event++; + sql_print_information("Forcing to reconnect slave I/O thread"); + if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings, + reconnect_messages[SLAVE_RECON_ACT_EVENT])) + goto err; + goto connected; + }); if (event_len == packet_error) { uint mysql_error_number= mysql_errno(mysql); - if (mysql_error_number == CR_NET_PACKET_TOO_LARGE) - { + switch (mysql_error_number) { + case CR_NET_PACKET_TOO_LARGE: sql_print_error("\ Log entry on master is longer than max_allowed_packet (%ld) on \ slave. If the entry is correct, restart the server with a higher value of \ max_allowed_packet", thd->variables.max_allowed_packet); goto err; - } - if (mysql_error_number == ER_MASTER_FATAL_ERROR_READING_BINLOG) - { + case ER_MASTER_FATAL_ERROR_READING_BINLOG: sql_print_error(ER(mysql_error_number), mysql_error_number, mysql_error(mysql)); goto err; - } - mi->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT; - thd->proc_info = "Waiting to reconnect after a failed master event read"; -#ifdef SIGNAL_WITH_VIO_CLOSE - thd->clear_active_vio(); -#endif - end_server(mysql); - if (retry_count++) - { - if (retry_count > master_retry_count) - goto err; // Don't retry forever - safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed, - (void*) mi); - } - if (io_slave_killed(thd,mi)) - { - if (global_system_variables.log_warnings) - sql_print_information("Slave I/O thread killed while waiting to \ -reconnect after a failed read"); + case EE_OUTOFMEMORY: + case ER_OUTOFMEMORY: + sql_print_error("\ +Stopping slave I/O thread due to out-of-memory error from master"); goto err; } - thd->proc_info = "Reconnecting after a failed master event read"; - if (!suppress_warnings) - sql_print_information("Slave I/O thread: Failed reading log event, \ -reconnecting to retry, log '%s' position %s", IO_RPL_LOG_NAME, - llstr(mi->master_log_pos, llbuff)); - if (safe_reconnect(thd, mysql, mi, suppress_warnings) || - io_slave_killed(thd,mi)) - { - if (global_system_variables.log_warnings) - sql_print_information("Slave I/O thread killed during or after a \ -reconnect done to recover from failed read"); + if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings, + reconnect_messages[SLAVE_RECON_ACT_EVENT])) goto err; - } goto connected; } // if (event_len == packet_error) @@ -2201,7 +2349,7 @@ err: /* Forget the relay log's format */ delete mi->rli.relay_log.description_event_for_queue; mi->rli.relay_log.description_event_for_queue= 0; - // TODO: make rpl_status part of MASTER_INFO + // TODO: make rpl_status part of Master_info change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE); DBUG_ASSERT(thd->net.buff != 0); net_end(&thd->net); // destructor will not free it, because net.vio is 0 @@ -2233,7 +2381,7 @@ pthread_handler_t handle_slave_sql(void *arg) THD *thd; /* needs to be first for thread_stack */ char llbuff[22],llbuff1[22]; - RELAY_LOG_INFO* rli = &((MASTER_INFO*)arg)->rli; + Relay_log_info* rli = &((Master_info*)arg)->rli; const char *errmsg; // needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff @@ -2502,7 +2650,7 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ process_io_create_file() */ -static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev) +static int process_io_create_file(Master_info* mi, Create_file_log_event* cev) { int error = 1; ulong num_bytes; @@ -2629,7 +2777,7 @@ err: */ -static int process_io_rotate(MASTER_INFO *mi, Rotate_log_event *rev) +static int process_io_rotate(Master_info *mi, Rotate_log_event *rev) { DBUG_ENTER("process_io_rotate"); safe_mutex_assert_owner(&mi->data_lock); @@ -2677,14 +2825,14 @@ static int process_io_rotate(MASTER_INFO *mi, Rotate_log_event *rev) Reads a 3.23 event and converts it to the slave's format. This code was copied from MySQL 4.0. */ -static int queue_binlog_ver_1_event(MASTER_INFO *mi, const char *buf, +static int queue_binlog_ver_1_event(Master_info *mi, const char *buf, ulong event_len) { const char *errmsg = 0; ulong inc_pos; bool ignore_event= 0; char *tmp_buf = 0; - RELAY_LOG_INFO *rli= &mi->rli; + Relay_log_info *rli= &mi->rli; DBUG_ENTER("queue_binlog_ver_1_event"); /* @@ -2795,13 +2943,13 @@ static int queue_binlog_ver_1_event(MASTER_INFO *mi, const char *buf, Reads a 4.0 event and converts it to the slave's format. This code was copied from queue_binlog_ver_1_event(), with some affordable simplifications. */ -static int queue_binlog_ver_3_event(MASTER_INFO *mi, const char *buf, +static int queue_binlog_ver_3_event(Master_info *mi, const char *buf, ulong event_len) { const char *errmsg = 0; ulong inc_pos; char *tmp_buf = 0; - RELAY_LOG_INFO *rli= &mi->rli; + Relay_log_info *rli= &mi->rli; DBUG_ENTER("queue_binlog_ver_3_event"); /* read_log_event() will adjust log_pos to be end_log_pos */ @@ -2859,7 +3007,7 @@ err: setup with 3.23 master or 4.0 master */ -static int queue_old_event(MASTER_INFO *mi, const char *buf, +static int queue_old_event(Master_info *mi, const char *buf, ulong event_len) { DBUG_ENTER("queue_old_event"); @@ -2887,11 +3035,11 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf, any >=5.0.0 format. */ -int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) +int queue_event(Master_info* mi,const char* buf, ulong event_len) { int error= 0; ulong inc_pos; - RELAY_LOG_INFO *rli= &mi->rli; + Relay_log_info *rli= &mi->rli; pthread_mutex_t *log_lock= rli->relay_log.get_log_lock(); DBUG_ENTER("queue_event"); @@ -3043,7 +3191,7 @@ err: } -void end_relay_log_info(RELAY_LOG_INFO* rli) +void end_relay_log_info(Relay_log_info* rli) { DBUG_ENTER("end_relay_log_info"); @@ -3087,7 +3235,7 @@ void end_relay_log_info(RELAY_LOG_INFO* rli) # Error */ -static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi) +static int safe_connect(THD* thd, MYSQL* mysql, Master_info* mi) { DBUG_ENTER("safe_connect"); @@ -3104,7 +3252,7 @@ static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi) master_retry_count times */ -static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi, +static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi, bool reconnect, bool suppress_warnings) { int slave_was_killed; @@ -3210,7 +3358,7 @@ replication resumed in log '%s' at position %s", mi->user, master_retry_count times */ -static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi, +static int safe_reconnect(THD* thd, MYSQL* mysql, Master_info* mi, bool suppress_warnings) { DBUG_ENTER("safe_reconnect"); @@ -3247,7 +3395,7 @@ static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi, 1 write error */ -bool flush_relay_log_info(RELAY_LOG_INFO* rli) +bool flush_relay_log_info(Relay_log_info* rli) { bool error=0; DBUG_ENTER("flush_relay_log_info"); @@ -3281,7 +3429,7 @@ bool flush_relay_log_info(RELAY_LOG_INFO* rli) Called when we notice that the current "hot" log got rotated under our feet. */ -static IO_CACHE *reopen_relay_log(RELAY_LOG_INFO *rli, const char **errmsg) +static IO_CACHE *reopen_relay_log(Relay_log_info *rli, const char **errmsg) { DBUG_ENTER("reopen_relay_log"); DBUG_ASSERT(rli->cur_log != &rli->cache_buf); @@ -3302,7 +3450,7 @@ static IO_CACHE *reopen_relay_log(RELAY_LOG_INFO *rli, const char **errmsg) } -static Log_event* next_event(RELAY_LOG_INFO* rli) +static Log_event* next_event(Relay_log_info* rli) { Log_event* ev; IO_CACHE* cur_log = rli->cur_log; @@ -3494,10 +3642,10 @@ static Log_event* next_event(RELAY_LOG_INFO* rli) // prevent the I/O thread from blocking next times rli->ignore_log_space_limit= 1; /* - If the I/O thread is blocked, unblock it. - Ok to broadcast after unlock, because the mutex is only destroyed in - ~st_relay_log_info(), i.e. when rli is destroyed, and rli will not be - destroyed before we exit the present function. + If the I/O thread is blocked, unblock it. Ok to broadcast + after unlock, because the mutex is only destroyed in + ~Relay_log_info(), i.e. when rli is destroyed, and rli will + not be destroyed before we exit the present function. */ pthread_mutex_unlock(&rli->log_space_lock); pthread_cond_broadcast(&rli->log_space_cond); @@ -3654,10 +3802,10 @@ err: is void). */ -void rotate_relay_log(MASTER_INFO* mi) +void rotate_relay_log(Master_info* mi) { DBUG_ENTER("rotate_relay_log"); - RELAY_LOG_INFO* rli= &mi->rli; + Relay_log_info* rli= &mi->rli; /* We don't lock rli->run_lock. This would lead to deadlocks. */ pthread_mutex_lock(&mi->run_lock); @@ -3698,11 +3846,11 @@ 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 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) +bool rpl_master_has_bug(Relay_log_info *rli, uint bug_id) { struct st_version_range_for_one_bug { uint bug_id; diff --git a/sql/slave.h b/sql/slave.h index 731728bde4f..2cd9ea352ba 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -16,6 +16,12 @@ #ifndef SLAVE_H #define SLAVE_H +/** + @defgroup Replication Replication + @{ + + @file +*/ #ifdef HAVE_REPLICATION #include "log.h" @@ -29,10 +35,9 @@ // Forward declarations -struct st_relay_log_info; -typedef st_relay_log_info RELAY_LOG_INFO; +class Relay_log_info; +class Master_info; -class MASTER_INFO; /***************************************************************************** @@ -43,11 +48,11 @@ class MASTER_INFO; I/O Thread - One of these threads is started for each master server. They maintain a connection to their master server, read log events from the master as they arrive, and queues them into - a single, shared relay log file. A MASTER_INFO + a single, shared relay log file. A Master_info represents each of these threads. SQL Thread - One of these threads is started and reads from the relay log - file, executing each event. A RELAY_LOG_INFO + file, executing each event. A Relay_log_info represents this thread. Buffering in the relay log file makes it unnecessary to reread events from @@ -70,18 +75,18 @@ class MASTER_INFO; mi->run_lock, keeps rli->run_lock, and tries to re-acquire mi->run_lock). Currently active_mi never moves (it's created at startup and deleted at - shutdown, and not changed: it always points to the same MASTER_INFO struct), + shutdown, and not changed: it always points to the same Master_info struct), because we don't have multimaster. So for the moment, mi does not move, and mi->rli does not either. - In MASTER_INFO: run_lock, data_lock + In Master_info: run_lock, data_lock run_lock protects all information about the run state: slave_running, and the existence of the I/O thread (to stop/start it, you need this mutex). data_lock protects some moving members of the struct: counters (log name, position) and relay log (MYSQL_BIN_LOG object). - In RELAY_LOG_INFO: run_lock, data_lock - see MASTER_INFO + In Relay_log_info: run_lock, data_lock + see Master_info Order of acquisition: if you want to have LOCK_active_mi and a run_lock, you must acquire LOCK_active_mi first. @@ -102,12 +107,12 @@ extern my_bool opt_log_slave_updates; extern ulonglong relay_log_space_limit; /* - 3 possible values for MASTER_INFO::slave_running and - RELAY_LOG_INFO::slave_running. + 3 possible values for Master_info::slave_running and + Relay_log_info::slave_running. The values 0,1,2 are very important: to keep the diff small, I didn't substitute places where we use 0/1 with the newly defined symbols. So don't change these values. - The same way, code is assuming that in RELAY_LOG_INFO we use only values + The same way, code is assuming that in Relay_log_info we use only values 0/1. I started with using an enum, but enum_variable=1; is not legal so would have required many line changes. @@ -129,16 +134,12 @@ extern ulonglong relay_log_space_limit; int init_slave(); void init_slave_skip_errors(const char* arg); -bool flush_relay_log_info(RELAY_LOG_INFO* rli); +bool flush_relay_log_info(Relay_log_info* rli); int register_slave_on_master(MYSQL* mysql); -int terminate_slave_threads(MASTER_INFO* mi, int thread_mask, +int terminate_slave_threads(Master_info* mi, int thread_mask, bool skip_lock = 0); -int terminate_slave_thread(THD* thd, pthread_mutex_t* term_mutex, - pthread_mutex_t* cond_lock, - pthread_cond_t* term_cond, - volatile uint* slave_running); int start_slave_threads(bool need_slave_mutex, bool wait_for_start, - MASTER_INFO* mi, const char* master_info_fname, + Master_info* mi, const char* master_info_fname, const char* slave_info_fname, int thread_mask); /* cond_lock is usually same as start_lock. It is needed for the case when @@ -151,7 +152,7 @@ int start_slave_thread(pthread_handler h_func, pthread_mutex_t* start_lock, pthread_cond_t* start_cond, volatile uint *slave_running, volatile ulong *slave_run_id, - MASTER_INFO* mi, + Master_info* mi, bool high_priority); /* If fd is -1, dump to NET */ @@ -160,37 +161,37 @@ int mysql_table_dump(THD* thd, const char* db, /* retrieve table from master and copy to slave*/ int fetch_master_table(THD* thd, const char* db_name, const char* table_name, - MASTER_INFO* mi, MYSQL* mysql, bool overwrite); + Master_info* mi, MYSQL* mysql, bool overwrite); -bool show_master_info(THD* thd, MASTER_INFO* mi); +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); +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); +int check_expected_error(THD* thd, Relay_log_info const *rli, int error_code); void skip_load_data_infile(NET* net); void end_slave(); /* clean up */ -void clear_until_condition(RELAY_LOG_INFO* rli); -void clear_slave_error(RELAY_LOG_INFO* rli); -void end_relay_log_info(RELAY_LOG_INFO* rli); -void lock_slave_threads(MASTER_INFO* mi); -void unlock_slave_threads(MASTER_INFO* mi); -void init_thread_mask(int* mask,MASTER_INFO* mi,bool inverse); -int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,ulonglong pos, +void clear_until_condition(Relay_log_info* rli); +void clear_slave_error(Relay_log_info* rli); +void end_relay_log_info(Relay_log_info* rli); +void lock_slave_threads(Master_info* mi); +void unlock_slave_threads(Master_info* mi); +void init_thread_mask(int* mask,Master_info* mi,bool inverse); +int init_relay_log_pos(Relay_log_info* rli,const char* log,ulonglong pos, bool need_data_lock, const char** errmsg, bool look_for_description_event); -int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset, +int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset, const char** errmsg); void set_slave_thread_options(THD* thd); -void set_slave_thread_default_charset(THD *thd, RELAY_LOG_INFO const *rli); -void rotate_relay_log(MASTER_INFO* mi); +void set_slave_thread_default_charset(THD *thd, Relay_log_info const *rli); +void rotate_relay_log(Master_info* mi); pthread_handler_t handle_slave_io(void *arg); pthread_handler_t handle_slave_sql(void *arg); extern bool volatile abort_loop; -extern MASTER_INFO main_mi, *active_mi; /* active_mi for multi-master */ +extern Master_info main_mi, *active_mi; /* active_mi for multi-master */ extern LIST master_list; extern my_bool replicate_same_server_id; @@ -214,6 +215,8 @@ extern I_List<THD> threads; #define SLAVE_IO 1 #define SLAVE_SQL 2 -#endif - +/** + @} (end of group Replication) +*/ +#endif diff --git a/sql/sp.cc b/sql/sp.cc index 66c6c05c930..6032688f7f1 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -84,7 +84,7 @@ class Stored_routine_creation_ctx : public Stored_program_creation_ctx, { public: static Stored_routine_creation_ctx * - load_from_db(THD *thd, const class sp_name *name, TABLE *proc_tbl); + load_from_db(THD *thd, const sp_name *name, TABLE *proc_tbl); public: virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root) @@ -97,7 +97,8 @@ public: protected: virtual Object_creation_ctx *create_backup_ctx(THD *thd) const { - return new Stored_routine_creation_ctx(thd); + DBUG_ENTER("Stored_routine_creation_ctx::create_backup_ctx"); + DBUG_RETURN(new Stored_routine_creation_ctx(thd)); } private: @@ -348,8 +349,8 @@ db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table) key_copy(key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx(table->record[0], 0, key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->index_read_idx_map(table->record[0], 0, key, HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) DBUG_RETURN(SP_KEY_NOT_FOUND); DBUG_RETURN(SP_OK); @@ -406,7 +407,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK) goto done; - if (table->s->fields != MYSQL_PROC_FIELD_COUNT) + if (table->s->fields < MYSQL_PROC_FIELD_COUNT) { ret= SP_GET_FIELD_FAILED; goto done; @@ -519,9 +520,10 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, { LEX *old_lex= thd->lex, newlex; String defstr; - char old_db_buf[NAME_LEN+1]; - LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) }; - bool dbchanged; + char saved_cur_db_name_buf[NAME_LEN+1]; + LEX_STRING saved_cur_db_name= + { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) }; + bool cur_db_changed; ulong old_sql_mode= thd->variables.sql_mode; ha_rows old_select_limit= thd->variables.select_limit; sp_rcontext *old_spcont= thd->spcont; @@ -566,16 +568,17 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, } /* - Change current database if needed. + Change the current database (if needed). - collation_database will be updated here. However, it can be wrong, - because it will contain the current value of the database collation. - We need collation_database to be fixed at the creation time -- so - we'll update it later in switch_query_ctx(). + TODO: why do we force switch here? */ - if ((ret= sp_use_new_db(thd, name->m_db, &old_db, 1, &dbchanged))) + if (mysql_opt_change_db(thd, &name->m_db, &saved_cur_db_name, TRUE, + &cur_db_changed)) + { + ret= SP_INTERNAL_ERROR; goto end; + } thd->spcont= NULL; @@ -584,25 +587,42 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, lex_start(thd); - if (parse_sql(thd, &lip, creation_ctx) || newlex.sphead == NULL) - { - sp_head *sp= newlex.sphead; + ret= parse_sql(thd, &lip, creation_ctx) || newlex.sphead == NULL; - if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE))) - goto end; - delete sp; - ret= SP_PARSE_ERROR; + /* + Force switching back to the saved current database (if changed), + because it may be NULL. In this case, mysql_change_db() would + generate an error. + */ + + if (cur_db_changed && mysql_change_db(thd, &saved_cur_db_name, TRUE)) + { + delete newlex.sphead; + ret= SP_INTERNAL_ERROR; + goto end; } - else + + if (ret) { - if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE))) - goto end; - *sphp= newlex.sphead; - (*sphp)->set_definer(&definer_user_name, &definer_host_name); - (*sphp)->set_info(created, modified, &chistics, sql_mode); - (*sphp)->set_creation_ctx(creation_ctx); - (*sphp)->optimize(); + delete newlex.sphead; + ret= SP_PARSE_ERROR; + goto end; } + + *sphp= newlex.sphead; + (*sphp)->set_definer(&definer_user_name, &definer_host_name); + (*sphp)->set_info(created, modified, &chistics, sql_mode); + (*sphp)->set_creation_ctx(creation_ctx); + (*sphp)->optimize(); + /* + Not strictly necessary to invoke this method here, since we know + that we've parsed CREATE PROCEDURE/FUNCTION and not an + UPDATE/DELETE/INSERT/REPLACE/LOAD/CREATE TABLE, but we try to + maintain the invariant that this method is called for each + distinct statement, in case its logic is extended with other + types of analyses in future. + */ + newlex.set_trg_event_type_for_tables(); } end: @@ -686,7 +706,7 @@ sp_create_routine(THD *thd, int type, sp_head *sp) strxnmov(definer, sizeof(definer)-1, thd->lex->definer->user.str, "@", thd->lex->definer->host.str, NullS); - if (table->s->fields != MYSQL_PROC_FIELD_COUNT) + if (table->s->fields < MYSQL_PROC_FIELD_COUNT) { ret= SP_GET_FIELD_FAILED; goto done; @@ -1058,7 +1078,7 @@ sp_show_status_routine(THD *thd, int type, const char *name_pattern) tables.db= (char*)"mysql"; tables.table_name= tables.alias= (char*)"proc"; - if (! (table= open_ltable(thd, &tables, TL_READ))) + if (! (table= open_ltable(thd, &tables, TL_READ, 0))) { res= SP_OPEN_TABLE_FAILED; goto done; @@ -1173,9 +1193,9 @@ sp_drop_db_routines(THD *thd, char *db) ret= SP_OK; table->file->ha_index_init(0, 1); - if (! table->file->index_read(table->record[0], - (uchar *)table->field[MYSQL_PROC_FIELD_DB]->ptr, - (key_part_map)1, HA_READ_KEY_EXACT)) + if (! table->file->index_read_map(table->record[0], + (uchar *)table->field[MYSQL_PROC_FIELD_DB]->ptr, + (key_part_map)1, HA_READ_KEY_EXACT)) { int nxtres; bool deleted= FALSE; @@ -1912,32 +1932,40 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, TABLE_LIST *table) { int ret= 0; - Table_triggers_list *triggers= table->table->triggers; - if (add_used_routine(lex, thd->stmt_arena, &triggers->sroutines_key, - table->belong_to_view)) + + Sroutine_hash_entry **last_cached_routine_ptr= + (Sroutine_hash_entry **)lex->sroutines_list.next; + + if (static_cast<int>(table->lock_type) >= + static_cast<int>(TL_WRITE_ALLOW_WRITE)) { - Sroutine_hash_entry **last_cached_routine_ptr= - (Sroutine_hash_entry **)lex->sroutines_list.next; for (int i= 0; i < (int)TRG_EVENT_MAX; i++) { - for (int j= 0; j < (int)TRG_ACTION_MAX; j++) + if (table->trg_event_map & + static_cast<uint8>(1 << static_cast<int>(i))) { - sp_head *trigger_body= triggers->bodies[i][j]; - if (trigger_body) + for (int j= 0; j < (int)TRG_ACTION_MAX; j++) { - (void)trigger_body-> - add_used_tables_to_table_list(thd, &lex->query_tables_last, - table->belong_to_view); - sp_update_stmt_used_routines(thd, lex, - &trigger_body->m_sroutines, - table->belong_to_view); - trigger_body->propagate_attributes(lex); + /* We can have only one trigger per action type currently */ + sp_head *trigger= table->table->triggers->bodies[i][j]; + if (trigger && + add_used_routine(lex, thd->stmt_arena, &trigger->m_sroutines_key, + table->belong_to_view)) + { + trigger->add_used_tables_to_table_list(thd, &lex->query_tables_last, + table->belong_to_view); + trigger->propagate_attributes(lex); + sp_update_stmt_used_routines(thd, lex, + &trigger->m_sroutines, + table->belong_to_view); + } } } } - ret= sp_cache_routines_and_add_tables_aux(thd, lex, - *last_cached_routine_ptr, FALSE); } + ret= sp_cache_routines_and_add_tables_aux(thd, lex, + *last_cached_routine_ptr, + FALSE); return ret; } @@ -2007,73 +2035,3 @@ create_string(THD *thd, String *buf, buf->append(body, bodylen); return TRUE; } - - - -/* - Change the current database if needed. - - 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 - to the buffer pointed at by str and length is updated - 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, - or an error has occurred. - - RETURN VALUE - 0 success - 1 access denied or out of memory (the error message is - set in THD) -*/ - -int -sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db, - bool no_access_check, bool *dbchangedp) -{ - int ret; - DBUG_ENTER("sp_use_new_db"); - DBUG_PRINT("enter", ("newdb: %s", new_db.str)); - - /* - Set new_db to an empty string if it's NULL, because mysql_change_db - requires a non-NULL argument. - new_db.str can be NULL only if we're restoring the old database after - execution of a stored procedure and there were no current database - selected. The stored procedure itself must always have its database - initialized. - */ - if (new_db.str == NULL) - new_db.str= empty_c_string; - - if (thd->db) - { - old_db->length= (strmake(old_db->str, thd->db, old_db->length) - - old_db->str); - } - else - { - old_db->str[0]= '\0'; - old_db->length= 0; - } - - /* Don't change the database if the new name is the same as the old one. */ - if (my_strcasecmp(system_charset_info, old_db->str, new_db.str) == 0) - { - *dbchangedp= FALSE; - DBUG_RETURN(0); - } - - ret= mysql_change_db(thd, &new_db, no_access_check); - - *dbchangedp= ret == 0; - DBUG_RETURN(ret); -} @@ -85,15 +85,4 @@ extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen, */ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup); - -/* - Do a "use new_db". The current db is stored at old_db. If new_db is the - same as the current one, nothing is changed. dbchangedp is set to true if - the db was actually changed. -*/ - -int -sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db, - bool no_access_check, bool *dbchangedp); - #endif /* _SP_H_ */ diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index 84b15ee15c4..cc6ba9ef1d0 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -214,8 +214,12 @@ void sp_cache_flush_obsolete(sp_cache **cp) Internal functions *************************************************************************/ -static uchar *hash_get_key_for_sp_head(const uchar *ptr, size_t *plen, - my_bool first) +extern "C" uchar *hash_get_key_for_sp_head(const uchar *ptr, size_t *plen, + my_bool first); +extern "C" void hash_free_sp_head(void *p); + +uchar *hash_get_key_for_sp_head(const uchar *ptr, size_t *plen, + my_bool first) { sp_head *sp= (sp_head *)ptr; *plen= sp->m_qname.length; @@ -223,8 +227,7 @@ static uchar *hash_get_key_for_sp_head(const uchar *ptr, size_t *plen, } -static void -hash_free_sp_head(void *p) +void hash_free_sp_head(void *p) { sp_head *sp= (sp_head *)p; delete sp; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index f0cc5204749..828517011d5 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -32,6 +32,8 @@ #include <my_user.h> +extern "C" uchar *sp_table_key(const uchar *ptr, size_t *plen, my_bool first); + Item_result sp_map_result_type(enum enum_field_types type) { @@ -247,11 +249,14 @@ sp_get_flags_for_command(LEX *lex) case SQLCOM_CREATE_TRIGGER: case SQLCOM_CREATE_USER: case SQLCOM_ALTER_TABLE: + case SQLCOM_GRANT: + case SQLCOM_REVOKE: case SQLCOM_BEGIN: case SQLCOM_RENAME_TABLE: case SQLCOM_RENAME_USER: case SQLCOM_DROP_INDEX: case SQLCOM_DROP_DB: + case SQLCOM_REVOKE_ALL: case SQLCOM_DROP_USER: case SQLCOM_DROP_VIEW: case SQLCOM_DROP_TRIGGER: @@ -349,13 +354,13 @@ sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr) enum_check_fields save_count_cuted_fields= thd->count_cuted_fields; bool save_abort_on_warning= thd->abort_on_warning; - bool save_no_trans_update_stmt= thd->no_trans_update.stmt; + bool save_stmt_modified_non_trans_table= thd->transaction.stmt.modified_non_trans_table; thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL; thd->abort_on_warning= thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES); - thd->no_trans_update.stmt= FALSE; + thd->transaction.stmt.modified_non_trans_table= FALSE; /* Save the value in the field. Convert the value if needed. */ @@ -363,7 +368,7 @@ sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr) thd->count_cuted_fields= save_count_cuted_fields; thd->abort_on_warning= save_abort_on_warning; - thd->no_trans_update.stmt= save_no_trans_update_stmt; + thd->transaction.stmt.modified_non_trans_table= save_stmt_modified_non_trans_table; if (thd->net.report_error) { @@ -483,7 +488,6 @@ sp_head::sp_head() */ m_db= m_name= m_qname= str_reset; - extern uchar *sp_table_key(const uchar *ptr, size_t *plen, my_bool first); DBUG_ENTER("sp_head::sp_head"); m_backpatch.empty(); @@ -512,12 +516,35 @@ sp_head::init(LEX *lex) */ lex->trg_table_fields.empty(); my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8); - m_param_begin= m_param_end= m_body_begin= 0; - m_qname.str= m_db.str= m_name.str= m_params.str= - m_body.str= m_defstr.str= 0; - m_qname.length= m_db.length= m_name.length= m_params.length= - m_body.length= m_defstr.length= 0; + + m_param_begin= NULL; + m_param_end= NULL; + + m_body_begin= NULL ; + + m_qname.str= NULL; + m_qname.length= 0; + + m_db.str= NULL; + m_db.length= 0; + + m_name.str= NULL; + m_name.length= 0; + + m_params.str= NULL; + m_params.length= 0; + + m_body.str= NULL; + m_body.length= 0; + + m_defstr.str= NULL; + m_defstr.length= 0; + + m_sroutines_key.str= NULL; + m_sroutines_key.length= 0; + m_return_field_def.charset= NULL; + DBUG_VOID_RETURN; } @@ -543,9 +570,14 @@ sp_head::init_sp_name(THD *thd, sp_name *spname) if (spname->m_qname.length == 0) spname->init_qname(thd); - m_qname.length= spname->m_qname.length; - m_qname.str= strmake_root(thd->mem_root, spname->m_qname.str, - m_qname.length); + m_sroutines_key.length= spname->m_sroutines_key.length; + m_sroutines_key.str= (char*) memdup_root(thd->mem_root, + spname->m_sroutines_key.str, + spname->m_sroutines_key.length + 1); + m_sroutines_key.str[0]= static_cast<char>(m_type); + + m_qname.length= m_sroutines_key.length - 1; + m_qname.str= m_sroutines_key.str + 1; DBUG_VOID_RETURN; } @@ -830,7 +862,8 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b) /* - Replace thd->query{_length} with a string that one can write to the binlog. + Replace thd->query{_length} with a string that one can write to the binlog + or the query cache. SYNOPSIS subst_spvars() @@ -842,7 +875,9 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b) DESCRIPTION The binlog-suitable string is produced by replacing references to SP local - variables with NAME_CONST('sp_var_name', value) calls. + variables with NAME_CONST('sp_var_name', value) calls. To make this string + suitable for the query cache this function allocates some additional space + for the query cache flags. RETURN FALSE on success @@ -855,80 +890,89 @@ static bool subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) { DBUG_ENTER("subst_spvars"); - if (thd->prelocked_mode == NON_PRELOCKED && mysql_bin_log.is_open()) - { - Dynamic_array<Item_splocal*> sp_vars_uses; - char *pbuf, *cur, buffer[512]; - String qbuf(buffer, sizeof(buffer), &my_charset_bin); - int prev_pos, res; - /* Find all instances of Item_splocal used in this statement */ - for (Item *item= instr->free_list; item; item= item->next) - { - if (item->is_splocal()) - { - Item_splocal *item_spl= (Item_splocal*)item; - if (item_spl->pos_in_query) - sp_vars_uses.append(item_spl); - } - } - if (!sp_vars_uses.elements()) - DBUG_RETURN(FALSE); - - /* Sort SP var refs by their occurences in the query */ - sp_vars_uses.sort(cmp_splocal_locations); + Dynamic_array<Item_splocal*> sp_vars_uses; + char *pbuf, *cur, buffer[512]; + String qbuf(buffer, sizeof(buffer), &my_charset_bin); + int prev_pos, res, buf_len; - /* - Construct a statement string where SP local var refs are replaced - with "NAME_CONST(name, value)" - */ - qbuf.length(0); - cur= query_str->str; - prev_pos= res= 0; - for (Item_splocal **splocal= sp_vars_uses.front(); - splocal < sp_vars_uses.back(); splocal++) + /* Find all instances of Item_splocal used in this statement */ + for (Item *item= instr->free_list; item; item= item->next) + { + if (item->is_splocal()) { - Item *val; - - char str_buffer[STRING_BUFFER_USUAL_SIZE]; - String str_value_holder(str_buffer, sizeof(str_buffer), - &my_charset_latin1); - String *str_value; - - /* append the text between sp ref occurences */ - res|= qbuf.append(cur + prev_pos, (*splocal)->pos_in_query - prev_pos); - prev_pos= (*splocal)->pos_in_query + (*splocal)->m_name.length; - - /* append the spvar substitute */ - res|= qbuf.append(STRING_WITH_LEN(" NAME_CONST('")); - res|= qbuf.append((*splocal)->m_name.str, (*splocal)->m_name.length); - res|= qbuf.append(STRING_WITH_LEN("',")); - res|= (*splocal)->fix_fields(thd, (Item **) splocal); + Item_splocal *item_spl= (Item_splocal*)item; + if (item_spl->pos_in_query) + sp_vars_uses.append(item_spl); + } + } + if (!sp_vars_uses.elements()) + DBUG_RETURN(FALSE); + + /* Sort SP var refs by their occurences in the query */ + sp_vars_uses.sort(cmp_splocal_locations); - if (res) - break; + /* + Construct a statement string where SP local var refs are replaced + with "NAME_CONST(name, value)" + */ + qbuf.length(0); + cur= query_str->str; + prev_pos= res= 0; + for (Item_splocal **splocal= sp_vars_uses.front(); + splocal < sp_vars_uses.back(); splocal++) + { + Item *val; + + char str_buffer[STRING_BUFFER_USUAL_SIZE]; + String str_value_holder(str_buffer, sizeof(str_buffer), + &my_charset_latin1); + String *str_value; + + /* append the text between sp ref occurences */ + res|= qbuf.append(cur + prev_pos, (*splocal)->pos_in_query - prev_pos); + prev_pos= (*splocal)->pos_in_query + (*splocal)->len_in_query; + + /* append the spvar substitute */ + res|= qbuf.append(STRING_WITH_LEN(" NAME_CONST('")); + res|= qbuf.append((*splocal)->m_name.str, (*splocal)->m_name.length); + res|= qbuf.append(STRING_WITH_LEN("',")); + res|= (*splocal)->fix_fields(thd, (Item **) splocal); - val= (*splocal)->this_item(); - 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 - res|= qbuf.append(STRING_WITH_LEN("NULL")); - res|= qbuf.append(')'); - if (res) - break; - } - res|= qbuf.append(cur + prev_pos, query_str->length - prev_pos); if (res) - DBUG_RETURN(TRUE); + break; - if (!(pbuf= thd->strmake(qbuf.ptr(), qbuf.length()))) - DBUG_RETURN(TRUE); + val= (*splocal)->this_item(); + 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 + res|= qbuf.append(STRING_WITH_LEN("NULL")); + res|= qbuf.append(')'); + if (res) + break; + } + res|= qbuf.append(cur + prev_pos, query_str->length - prev_pos); + if (res) + DBUG_RETURN(TRUE); - thd->query= pbuf; - thd->query_length= qbuf.length(); + /* + Allocate additional space at the end of the new query string for the + query_cache_send_result_to_client function. + */ + buf_len= qbuf.length() + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE + 1; + if ((pbuf= (char *) alloc_root(thd->mem_root, buf_len))) + { + memcpy(pbuf, qbuf.ptr(), qbuf.length()); + pbuf[qbuf.length()]= 0; } + else + DBUG_RETURN(TRUE); + + thd->query= pbuf; + thd->query_length= qbuf.length(); + DBUG_RETURN(FALSE); } @@ -972,9 +1016,10 @@ bool sp_head::execute(THD *thd) { DBUG_ENTER("sp_head::execute"); - char old_db_buf[NAME_LEN+1]; - LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) }; - bool dbchanged; + char saved_cur_db_name_buf[NAME_LEN+1]; + LEX_STRING saved_cur_db_name= + { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) }; + bool cur_db_changed= FALSE; sp_rcontext *ctx; bool err_status= FALSE; uint ip= 0; @@ -1029,8 +1074,11 @@ sp_head::execute(THD *thd) */ if (m_db.length && - (err_status= sp_use_new_db(thd, m_db, &old_db, 0, &dbchanged))) + (err_status= mysql_opt_change_db(thd, &m_db, &saved_cur_db_name, FALSE, + &cur_db_changed))) + { goto done; + } if ((ctx= thd->spcont)) ctx->clear_handler(); @@ -1038,6 +1086,12 @@ sp_head::execute(THD *thd) old_arena= thd->stmt_arena; /* + Switch query context. This has to be done early as this is sometimes + allocated trough sql_alloc + */ + saved_creation_ctx= m_creation_ctx->set_n_backup(thd); + + /* We have to save/restore this info when we are changing call level to be able properly do close_thread_tables() in instructions. */ @@ -1082,10 +1136,6 @@ sp_head::execute(THD *thd) */ thd->spcont->callers_arena= &backup_arena; - /* Switch query context. */ - - saved_creation_ctx= m_creation_ctx->set_n_backup(thd); - do { sp_instr *i; @@ -1209,14 +1259,14 @@ sp_head::execute(THD *thd) If the DB has changed, the pointer has changed too, but the original thd->db will then have been freed */ - if (dbchanged) + if (cur_db_changed && !thd->killed) { /* - No access check when changing back to where we came from. - (It would generate an error from mysql_change_db() when old_db=="") + Force switching back to the saved current database, because it may be + NULL. In this case, mysql_change_db() would generate an error. */ - if (! thd->killed) - err_status|= mysql_change_db(thd, &old_db, TRUE); + + err_status|= mysql_change_db(thd, &saved_cur_db_name, TRUE); } m_flags&= ~IS_INVOKED; DBUG_PRINT("info", @@ -1924,8 +1974,11 @@ sp_head::restore_lex(THD *thd) { DBUG_ENTER("sp_head::restore_lex"); LEX *sublex= thd->lex; - LEX *oldlex= (LEX *)m_lex.pop(); + LEX *oldlex; + sublex->set_trg_event_type_for_tables(); + + oldlex= (LEX *)m_lex.pop(); if (! oldlex) return; // Nothing to restore @@ -2260,7 +2313,8 @@ sp_head::show_create_routine(THD *thd, int type) protocol->store(sql_mode.str, sql_mode.length, system_charset_info); if (full_access) - protocol->store(m_defstr.str, m_defstr.length, &my_charset_bin); + protocol->store(m_defstr.str, m_defstr.length, + m_creation_ctx->get_client_cs()); else protocol->store_null(); @@ -2503,6 +2557,13 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, int res= 0; DBUG_ENTER("reset_lex_and_exec_core"); + /* + The flag is saved at the entry to the following substatement. + It's reset further in the common code part. + It's merged with the saved parent's value at the exit of this func. + */ + bool parent_modified_non_trans_table= thd->transaction.stmt.modified_non_trans_table; + thd->transaction.stmt.modified_non_trans_table= FALSE; DBUG_ASSERT(!thd->derived_tables); DBUG_ASSERT(thd->change_list.is_empty()); /* @@ -2572,7 +2633,11 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, /* Update the state of the active arena. */ thd->stmt_arena->state= Query_arena::EXECUTED; - + /* + Merge here with the saved parent's values + what is needed from the substatement gained + */ + thd->transaction.stmt.modified_non_trans_table |= parent_modified_non_trans_table; /* Unlike for PS we should not call Item's destructors for newly created items after execution of each instruction in stored routine. This is @@ -3544,10 +3609,11 @@ typedef struct st_sp_table thr_lock_type lock_type; /* lock type used for prelocking */ uint lock_count; uint query_lock_count; + uint8 trg_event_map; } SP_TABLE; -uchar * -sp_table_key(const uchar *ptr, size_t *plen, my_bool first) + +uchar *sp_table_key(const uchar *ptr, size_t *plen, my_bool first) { SP_TABLE *tab= (SP_TABLE *)ptr; *plen= tab->qname.length; @@ -3630,6 +3696,7 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check) tab->query_lock_count++; if (tab->query_lock_count > tab->lock_count) tab->lock_count++; + tab->trg_event_map|= table->trg_event_map; } else { @@ -3651,6 +3718,7 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check) tab->db_length= table->db_length; tab->lock_type= table->lock_type; tab->lock_count= tab->query_lock_count= 1; + tab->trg_event_map= table->trg_event_map; my_hash_insert(&m_sptabs, (uchar *)tab); } } @@ -3728,6 +3796,7 @@ sp_head::add_used_tables_to_table_list(THD *thd, table->cacheable_table= 1; table->prelocking_placeholder= 1; table->belong_to_view= belong_to_view; + table->trg_event_map= stab->trg_event_map; /* Everyting else should be zeroed */ diff --git a/sql/sp_head.h b/sql/sp_head.h index 490fda67bfe..3a8b41acd4c 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -23,6 +23,11 @@ #include <stddef.h> +/** + @defgroup Stored_Routines Stored Routines + @ingroup Runtime_Environment + @{ +*/ // Values for the type enum. This reflects the order of the enum declaration // in the CREATE TABLE command. #define TYPE_ENUM_FUNCTION 1 @@ -184,6 +189,12 @@ public: st_sp_chistics *m_chistics; ulong m_sql_mode; // For SHOW CREATE and execution LEX_STRING m_qname; // db.name + /** + Key representing routine in the set of stored routines used by statement. + [routine_type]db.name + @sa sp_name::m_sroutines_key + */ + LEX_STRING m_sroutines_key; LEX_STRING m_db; LEX_STRING m_name; LEX_STRING m_params; @@ -1316,4 +1327,8 @@ sp_prepare_func_item(THD* thd, Item **it_addr); bool sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr); +/** + @} (end of group Stored_Routines) +*/ + #endif /* _SP_HEAD_H_ */ diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index b94c733cb0a..60a0c962c28 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -37,6 +37,7 @@ sp_rcontext::sp_rcontext(sp_pcontext *root_parsing_ctx, m_var_items(0), m_return_value_fld(return_value_fld), m_return_value_set(FALSE), + in_sub_stmt(FALSE), m_hcount(0), m_hsp(0), m_ihsp(0), @@ -67,6 +68,8 @@ sp_rcontext::~sp_rcontext() bool sp_rcontext::init(THD *thd) { + in_sub_stmt= thd->in_sub_stmt; + if (init_var_table(thd) || init_var_items()) return TRUE; @@ -191,7 +194,7 @@ sp_rcontext::set_return_value(THD *thd, Item **return_value_item) */ bool -sp_rcontext::find_handler(uint sql_errno, +sp_rcontext::find_handler(THD *thd, uint sql_errno, MYSQL_ERROR::enum_warning_level level) { if (m_hfound >= 0) @@ -200,6 +203,15 @@ sp_rcontext::find_handler(uint sql_errno, const char *sqlstate= mysql_errno_to_sqlstate(sql_errno); int i= m_hcount, found= -1; + /* + If this is a fatal sub-statement error, and this runtime + context corresponds to a sub-statement, no CONTINUE/EXIT + handlers from this context are applicable: try to locate one + in the outer scope. + */ + if (thd->is_fatal_sub_stmt_error && in_sub_stmt) + i= 0; + /* Search handlers from the latest (innermost) to the oldest (outermost) */ while (i--) { @@ -252,7 +264,7 @@ sp_rcontext::find_handler(uint sql_errno, */ if (m_prev_runtime_ctx && IS_EXCEPTION_CONDITION(sqlstate) && level == MYSQL_ERROR::WARN_LEVEL_ERROR) - return m_prev_runtime_ctx->find_handler(sql_errno, level); + return m_prev_runtime_ctx->find_handler(thd, sql_errno, level); return FALSE; } m_hfound= found; @@ -298,7 +310,7 @@ sp_rcontext::handle_error(uint sql_errno, elevated_level= MYSQL_ERROR::WARN_LEVEL_ERROR; } - if (find_handler(sql_errno, elevated_level)) + if (find_handler(thd, sql_errno, elevated_level)) { if (elevated_level == MYSQL_ERROR::WARN_LEVEL_ERROR) { diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index fbf479f52aa..0104b71a648 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -125,7 +125,7 @@ class sp_rcontext : public Sql_alloc // Returns 1 if a handler was found, 0 otherwise. bool - find_handler(uint sql_errno,MYSQL_ERROR::enum_warning_level level); + find_handler(THD *thd, uint sql_errno,MYSQL_ERROR::enum_warning_level level); // If there is an error handler for this error, handle it and return TRUE. bool @@ -236,6 +236,10 @@ private: during execution. */ bool m_return_value_set; + /** + TRUE if the context is created for a sub-statement. + */ + bool in_sub_stmt; sp_handler_t *m_handler; // Visible handlers uint m_hcount; // Stack pointer for m_handler diff --git a/sql/spatial.cc b/sql/spatial.cc index e0680ed182c..97e5fcfa27a 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -17,7 +17,28 @@ #ifdef HAVE_SPATIAL -#define MAX_DIGITS_IN_DOUBLE 16 +/* + exponential notation : + 1 sign + 1 number before the decimal point + 1 decimal point + 14 number of significant digits (see String::qs_append(double)) + 1 'e' sign + 1 exponent sign + 3 exponent digits + == + 22 + + "f" notation : + 1 optional 0 + 1 sign + 14 number significant digits (see String::qs_append(double) ) + 1 decimal point + == + 17 +*/ + +#define MAX_DIGITS_IN_DOUBLE 22 /***************************** Gis_class_info *******************************/ diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 67fa380d313..7e017d7d028 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1602,7 +1602,7 @@ bool change_password(THD *thd, const char *host, const char *user, } #endif - if (!(table= open_ltable(thd, &tables, TL_WRITE))) + if (!(table= open_ltable(thd, &tables, TL_WRITE, 0))) DBUG_RETURN(1); VOID(pthread_mutex_lock(&acl_cache->lock)); @@ -1815,9 +1815,9 @@ static bool update_user_table(THD *thd, TABLE *table, key_copy((uchar *) user_key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx(table->record[0], 0, - (uchar *) user_key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->index_read_idx_map(table->record[0], 0, + (uchar *) user_key, HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0)); /* purecov: deadcode */ @@ -1908,8 +1908,9 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx(table->record[0], 0, user_key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->index_read_idx_map(table->record[0], 0, user_key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { /* what == 'N' means revoke */ if (what == 'N') @@ -2131,8 +2132,9 @@ static int replace_db_table(TABLE *table, const char *db, key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx(table->record[0],0, user_key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->index_read_idx_map(table->record[0],0, user_key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { if (what == 'N') { // no row, no revoke @@ -2348,8 +2350,8 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) col_privs->field[4]->store("",0, &my_charset_latin1); col_privs->file->ha_index_init(0, 1); - if (col_privs->file->index_read(col_privs->record[0], (uchar*) key, - (key_part_map)15, HA_READ_KEY_EXACT)) + if (col_privs->file->index_read_map(col_privs->record[0], (uchar*) key, + (key_part_map)15, HA_READ_KEY_EXACT)) { cols = 0; /* purecov: deadcode */ col_privs->file->ha_index_end(); @@ -2511,8 +2513,8 @@ static int replace_column_table(GRANT_TABLE *g_t, key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read(table->record[0], user_key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->index_read_map(table->record[0], user_key, HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { if (revoke_grant) { @@ -2589,8 +2591,9 @@ static int replace_column_table(GRANT_TABLE *g_t, key_copy(user_key, table->record[0], table->key_info, key_prefix_length); - if (table->file->index_read(table->record[0], user_key, (key_part_map)15, - HA_READ_KEY_EXACT)) + if (table->file->index_read_map(table->record[0], user_key, + (key_part_map)15, + HA_READ_KEY_EXACT)) goto end; /* Scan through all rows with the same host,db,user and table */ @@ -2691,8 +2694,9 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx(table->record[0], 0, user_key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->index_read_idx_map(table->record[0], 0, user_key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { /* The following should never happen as we first check the in memory @@ -2816,9 +2820,10 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, TRUE); store_record(table,record[1]); // store at pos 1 - if (table->file->index_read_idx(table->record[0], 0, - (uchar*) table->field[0]->ptr, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->index_read_idx_map(table->record[0], 0, + (uchar*) table->field[0]->ptr, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { /* The following should never happen as we first check the in memory @@ -5015,9 +5020,9 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop, table->key_info->key_part[1].store_length); key_copy(user_key, table->record[0], table->key_info, key_prefix_length); - if ((error= table->file->index_read_idx(table->record[0], 0, - user_key, (key_part_map)3, - HA_READ_KEY_EXACT))) + if ((error= table->file->index_read_idx_map(table->record[0], 0, + user_key, (key_part_map)3, + HA_READ_KEY_EXACT))) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) { diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index 6b4aaa26236..490cc5e28c1 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -668,7 +668,7 @@ void field_ulonglong::add() } // field_ulonglong::add -int analyse::send_row(List<Item> &field_list __attribute__((unused))) +int analyse::send_row(List<Item> & /* field_list */) { field_info **f = f_info; diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h index ae5d88bf93e..827b6f4b217 100644 --- a/sql/sql_analyse.h +++ b/sql/sql_analyse.h @@ -348,7 +348,7 @@ public: } virtual void add() {} virtual bool change_columns(List<Item> &fields); - virtual int send_row(List<Item> &fields); + virtual int send_row(List<Item> &field_list); virtual void end_group(void) {} virtual bool end_of_records(void); friend Procedure *proc_analyse_init(THD *thd, ORDER *param, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index fb5fd2ec627..337fde53dac 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -64,11 +64,11 @@ Prelock_error_handler::handle_error(uint sql_errno, if (sql_errno == ER_NO_SUCH_TABLE) { m_handled_errors++; - return TRUE; // 'TRUE', as per coding style + return TRUE; } m_unhandled_errors++; - return FALSE; // 'FALSE', as per coding style + return FALSE; } @@ -82,6 +82,10 @@ bool Prelock_error_handler::safely_trapped_errors() return ((m_handled_errors > 0) && (m_unhandled_errors == 0)); } +/** + @defgroup Data_Dictionary Data Dictionary + @{ +*/ TABLE *unused_tables; /* Used by mysql_test */ HASH open_cache; /* Used by mysql_test */ @@ -902,8 +906,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, bool found=0; for (TABLE_LIST *table= tables; table; table= table->next_local) { - if ((!table->table || !table->table->s->log_table) && - remove_table_from_cache(thd, table->db, table->table_name, + if (remove_table_from_cache(thd, table->db, table->table_name, RTFC_OWNED_BY_THD_FLAG)) found=1; } @@ -951,8 +954,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, are employed by CREATE TABLE as in this case table simply does not exist yet. */ - if (!table->s->log_table && - (table->needs_reopen_or_name_lock() && table->db_stat)) + if (table->needs_reopen_or_name_lock() && table->db_stat) { found=1; DBUG_PRINT("signal", ("Waiting for COND_refresh")); @@ -1219,13 +1221,6 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) 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) found_old_table|= close_thread_table(thd, &thd->open_tables); @@ -1316,24 +1311,20 @@ 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; + LINT_INIT(next); if (!thd->temporary_tables) return; if (!mysql_bin_log.is_open() || thd->current_stmt_binlog_row_based) { - TABLE *next; - for (table= thd->temporary_tables; table; table= next) + TABLE *tmp_next; + for (table= thd->temporary_tables; table; table= tmp_next) { - next=table->next; + tmp_next= table->next; close_temporary(table, 1, 1); } thd->temporary_tables= 0; @@ -1346,13 +1337,12 @@ void close_temporary_tables(THD *thd) char buf[256]; String s_query= String(buf, sizeof(buf), system_charset_info); 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 + Insertion sort of temp tables by pseudo_thread_id to build ordered list + of sublists of equal pseudo_thread_id */ for (prev_table= thd->temporary_tables, table= prev_table->next; @@ -1476,7 +1466,7 @@ void close_temporary_tables(THD *thd) */ TABLE_LIST *find_table_in_list(TABLE_LIST *table, - st_table_list *TABLE_LIST::*link, + TABLE_LIST *TABLE_LIST::*link, const char *db_name, const char *table_name) { @@ -1679,8 +1669,28 @@ TABLE *find_temporary_table(THD *thd, TABLE_LIST *table_list) } -/* - Close temporary table and unlink from thd->temporary tables +/** + Drop a temporary table. + + Try to locate the table in the list of thd->temporary_tables. + If the table is found: + - if the table is in thd->locked_tables, unlock it and + remove it from the list of locked tables. Currently only transactional + temporary tables are present in the locked_tables list. + - Close the temporary table, remove its .FRM + - remove the table from the list of temporary tables + + This function is used to drop user temporary tables, as well as + internal tables created in CREATE TEMPORARY TABLE ... SELECT + or ALTER TABLE. Even though part of the work done by this function + is redundant when the table is internal, as long as we + link both internal and user temporary tables into the same + thd->temporary_tables list, it's impossible to tell here whether + we're dealing with an internal or a user temporary table. + + @retval TRUE the table was not found in the list of temporary tables + of this thread + @retval FALSE the table was found and dropped successfully. */ bool close_temporary_table(THD *thd, TABLE_LIST *table_list) @@ -1689,6 +1699,11 @@ bool close_temporary_table(THD *thd, TABLE_LIST *table_list) if (!(table= find_temporary_table(thd, table_list))) return 1; + /* + If LOCK TABLES list is not empty and contains this table, + unlock the table and remove the table from this list. + */ + mysql_lock_remove(thd, thd->locked_tables, table, FALSE); close_temporary_table(thd, table, 1, 1); return 0; } @@ -1835,7 +1850,7 @@ void unlink_open_table(THD *thd, TABLE *find, bool unlock) !memcmp(list->s->table_cache_key.str, key, key_length)) { if (unlock && thd->locked_tables) - mysql_lock_remove(thd, thd->locked_tables,list); + mysql_lock_remove(thd, thd->locked_tables, list, TRUE); VOID(hash_delete(&open_cache,(uchar*) list)); // Close table } else @@ -1861,8 +1876,13 @@ void unlink_open_table(THD *thd, TABLE *find, bool unlock) @note This routine assumes that table to be closed is open only by calling thread so we needn't wait until other threads - will close the table. It also assumes that table to be - dropped is already unlocked. + will close the table. Also unless called under implicit or + explicit LOCK TABLES mode it assumes that table to be + dropped is already unlocked. In the former case it will + also remove lock on the table. But one should not rely on + this behaviour as it may change in future. + Currently, however, this function is never called for a + table that was locked with LOCK TABLES. */ void drop_open_table(THD *thd, TABLE *table, const char *db_name, @@ -2125,9 +2145,9 @@ bool lock_table_name_if_not_cached(THD *thd, const char *db, @brief Check that table exists in table definition cache, on disk or in some storage engine. - @param thd Thread context - @param table Table list element - @param exists[out] Out parameter which is set to TRUE if table + @param thd Thread context + @param table Table list element + @param[out] exists Out parameter which is set to TRUE if table exists and to FALSE otherwise. @note This function assumes that caller owns LOCK_open mutex. @@ -2228,7 +2248,6 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, HASH_SEARCH_STATE state; DBUG_ENTER("open_table"); - DBUG_ASSERT (table_list->lock_type != TL_WRITE_DEFAULT); /* find a unused table in the open table cache */ if (refresh) *refresh=0; @@ -2277,7 +2296,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } table->query_id= thd->query_id; table->clear_query_id= 1; - thd->tmp_table_used= 1; + thd->thread_specific_used= TRUE; DBUG_PRINT("info",("Using temporary table")); goto reset; } @@ -2468,8 +2487,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, &state)) { /* - Here we flush tables marked for flush. However we never flush log - tables here. They are flushed only on FLUSH LOGS. + Here we flush tables marked for flush. Normally, table->s->version contains the value of refresh_version from the moment when this table was (re-)opened and added to the cache. @@ -2486,7 +2504,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, c1: name lock t2; -- blocks c2: open t1; -- blocks */ - if (table->needs_reopen_or_name_lock() && !table->s->log_table) + if (table->needs_reopen_or_name_lock()) { DBUG_PRINT("note", ("Found table '%s.%s' with different refresh version", @@ -2732,11 +2750,10 @@ bool reopen_table(TABLE *table) sql_print_error("Table %s had a open data handler in reopen_table", table->alias); #endif + bzero((char*) &table_list, sizeof(TABLE_LIST)); table_list.db= table->s->db.str; table_list.table_name= table->s->table_name.str; table_list.table= table; - table_list.belong_to_view= 0; - table_list.next_local= 0; if (wait_for_locked_table_names(thd, &table_list)) DBUG_RETURN(1); // Thread was killed @@ -2840,7 +2857,7 @@ void close_data_files_and_morph_locks(THD *thd, const char *db, !strcmp(table->s->db.str, db)) { if (thd->locked_tables) - mysql_lock_remove(thd, thd->locked_tables, table); + mysql_lock_remove(thd, thd->locked_tables, table, TRUE); table->open_placeholder= 1; close_handle_and_leave_table_as_lock(table); } @@ -2962,10 +2979,9 @@ void close_old_data_files(THD *thd, TABLE *table, bool morph_locks, for (; table ; table=table->next) { /* - Reopen marked for flush. But close log tables. They are flushed only - explicitly on FLUSH LOGS + Reopen marked for flush. */ - if (table->needs_reopen_or_name_lock() && !table->s->log_table) + if (table->needs_reopen_or_name_lock()) { found=1; if (table->db_stat) @@ -2979,7 +2995,7 @@ void close_old_data_files(THD *thd, TABLE *table, bool morph_locks, instances of this table. */ mysql_lock_abort(thd, table, TRUE); - mysql_lock_remove(thd, thd->locked_tables, table); + mysql_lock_remove(thd, thd->locked_tables, table, TRUE); /* We want to protect the table from concurrent DDL operations (like RENAME TABLE) until we will re-open and re-lock it. @@ -3012,10 +3028,6 @@ void close_old_data_files(THD *thd, TABLE *table, bool morph_locks, Wait until all threads has closed the tables in the list We have also to wait if there is thread that has a lock on this table even if the table is closed - NOTE: log tables are handled differently by the logging routines. - E.g. general_log is always opened and locked by the logger - and the table handler used by the logger, will be skipped by - this check. */ bool table_is_used(TABLE *table, bool wait_for_name_lock) @@ -3034,10 +3046,10 @@ bool table_is_used(TABLE *table, bool wait_for_name_lock) search= (TABLE*) hash_next(&open_cache, (uchar*) key, key_length, &state)) { - DBUG_PRINT("info", ("share: 0x%lx locked_by_logger: %d " + DBUG_PRINT("info", ("share: 0x%lx " "open_placeholder: %d locked_by_name: %d " "db_stat: %u version: %lu", - (ulong) search->s, search->locked_by_logger, + (ulong) search->s, search->open_placeholder, search->locked_by_name, search->db_stat, search->s->version)); @@ -3049,12 +3061,9 @@ bool table_is_used(TABLE *table, bool wait_for_name_lock) - If we are in flush table and we didn't execute the flush - If the table engine is open and it's an old version (We must wait until all engines are shut down to use the table) - However we fo not wait if we encountered a table, locked by the logger. - Log tables are managed separately by logging routines. */ - if (!search->locked_by_logger && - (search->locked_by_name && wait_for_name_lock || - (search->is_name_opened() && search->needs_reopen_or_name_lock()))) + if ( (search->locked_by_name && wait_for_name_lock) || + (search->is_name_opened() && search->needs_reopen_or_name_lock())) DBUG_RETURN(1); } } while ((table=table->next)); @@ -3131,7 +3140,7 @@ TABLE *drop_locked_tables(THD *thd,const char *db, const char *table_name) if (!strcmp(table->s->table_name.str, table_name) && !strcmp(table->s->db.str, db)) { - mysql_lock_remove(thd, thd->locked_tables,table); + mysql_lock_remove(thd, thd->locked_tables, table, TRUE); if (!found) { found= table; @@ -3276,15 +3285,19 @@ static int open_unireg_entry(THD *thd, TABLE *entry, TABLE_LIST *table_list, DBUG_ENTER("open_unireg_entry"); safe_mutex_assert_owner(&LOCK_open); - retry: if (!(share= get_table_share_with_create(thd, table_list, cache_key, cache_key_length, - OPEN_VIEW, &error))) + OPEN_VIEW | + table_list->i_s_requested_object, + &error))) DBUG_RETURN(1); if (share->is_view) { + if (table_list->i_s_requested_object & OPEN_TABLE_ONLY) + goto err; + /* Open view */ error= (int) open_new_frm(thd, share, alias, (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | @@ -3300,6 +3313,9 @@ retry: DBUG_RETURN((flags & OPEN_VIEW_NO_PARSE)? -1 : 0); } + if (table_list->i_s_requested_object & OPEN_VIEW_ONLY) + goto err; + while ((error= open_table_from_share(thd, share, alias, (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | @@ -3533,13 +3549,8 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) */ for (tables= *start; tables ;tables= tables->next_global) { - safe_to_ignore_table= FALSE; // 'FALSE', as per coding style + safe_to_ignore_table= FALSE; - if (tables->lock_type == TL_WRITE_DEFAULT) - { - tables->lock_type= thd->update_lock_default; - DBUG_ASSERT (tables->lock_type >= TL_WRITE_ALLOW_WRITE); - } /* Ignore placeholders for derived tables. After derived tables processing, link to created temporary table will be put here. @@ -3684,7 +3695,8 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) } if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables) - tables->table->reginfo.lock_type=tables->lock_type; + tables->table->reginfo.lock_type= tables->lock_type == TL_WRITE_DEFAULT ? + thd->update_lock_default : tables->lock_type; tables->table->grant= tables->grant; process_view_routines: @@ -3766,6 +3778,7 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table, thd Thread handler table_list Table to open is first table in this list lock_type Lock to use for open + lock_flags Flags passed to mysql_lock_table NOTE This function don't do anything like SP/SF/views/triggers analysis done @@ -3781,7 +3794,8 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table, table_list->table table */ -TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) +TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, + uint lock_flags) { TABLE *table; bool refresh; @@ -3797,13 +3811,6 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) if (table) { -#if defined( __WIN__) - /* Win32 can't drop a file that is open */ - if (lock_type == TL_WRITE_ALLOW_READ) - { - lock_type= TL_WRITE; - } -#endif /* __WIN__ */ table_list->lock_type= lock_type; table_list->table= table; table->grant= table_list->grant; @@ -3816,8 +3823,8 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) { DBUG_ASSERT(thd->lock == 0); // You must lock everything at once if ((table->reginfo.lock_type= lock_type) != TL_UNLOCK) - if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1, 0, - &refresh))) + if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1, + lock_flags, &refresh))) table= 0; } } @@ -4156,11 +4163,6 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) DBUG_ASSERT(thd->lock == 0); // You must lock everything at once TABLE **start,**ptr; uint lock_flag= MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN; - - /* Ignore GLOBAL READ LOCK and GLOBAL READ_ONLY if called from a logger */ - if (logger.is_privileged_thread(thd)) - lock_flag|= (MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK | - MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY); if (!(ptr=start=(TABLE**) thd->alloc(sizeof(TABLE*)*count))) DBUG_RETURN(-1); @@ -4505,8 +4507,7 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list, table_list->alias, name, item_name, (ulong) ref)); Field_iterator_view field_it; field_it.set(table_list); - Query_arena *arena, backup; - LINT_INIT(arena); + Query_arena *arena= 0, backup; DBUG_ASSERT(table_list->schema_table_reformed || (ref != 0 && table_list->view != 0)); @@ -4515,14 +4516,14 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list, if (!my_strcasecmp(system_charset_info, field_it.name(), name)) { // in PS use own arena or data will be freed after prepare - if (register_tree_change) + if (register_tree_change && thd->stmt_arena->is_stmt_prepare_or_first_sp_execute()) arena= thd->activate_stmt_arena_if_needed(&backup); /* create_item() may, or may not create a new Item, depending on the column reference. See create_view_field() for details. */ Item *item= field_it.create_item(thd); - if (register_tree_change && arena) + if (arena) thd->restore_active_arena(arena, &backup); if (!item) @@ -6729,6 +6730,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); select_lex->cond_count= 0; select_lex->between_count= 0; + select_lex->max_equal_elems= 0; for (table= tables; table; table= table->next_local) { @@ -7189,7 +7191,12 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, else if (in_use != thd) { DBUG_PRINT("info", ("Table was in use by other thread")); - in_use->some_tables_deleted=1; + /* + Mark that table is going to be deleted from cache. This will + force threads that are in mysql_lock_tables() (but not yet + in thr_multi_lock()) to abort it's locks, close all tables and retry + */ + in_use->some_tables_deleted= 1; if (table->is_name_opened()) { DBUG_PRINT("info", ("Found another active instance of the table")); @@ -7625,7 +7632,7 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, if (!table) goto error; - DBUG_ASSERT(table->s->system_table); + DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_SYSTEM); table->use_all_columns(); table->reginfo.lock_type= tables->lock_type; @@ -7692,12 +7699,106 @@ open_system_table_for_update(THD *thd, TABLE_LIST *one_table) { DBUG_ENTER("open_system_table_for_update"); - TABLE *table= open_ltable(thd, one_table, one_table->lock_type); + TABLE *table= open_ltable(thd, one_table, one_table->lock_type, 0); if (table) { - DBUG_ASSERT(table->s->system_table); + DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_SYSTEM); + table->use_all_columns(); + } + + DBUG_RETURN(table); +} + +/** + Open a performance schema table. + Opening such tables is performed internally in the server + implementation, and is a 'nested' open, since some tables + might be already opened by the current thread. + The thread context before this call is saved, and is restored + when calling close_performance_schema_table(). + @param thd The current thread + @param one_table Performance schema table to open + @param backup [out] Temporary storage used to save the thread context +*/ +TABLE * +open_performance_schema_table(THD *thd, TABLE_LIST *one_table, + Open_tables_state *backup) +{ + uint flags= ( MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK | + MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY | + MYSQL_LOCK_IGNORE_FLUSH | + MYSQL_LOCK_PERF_SCHEMA); + TABLE *table; + /* Save value that is changed in mysql_lock_tables() */ + ulonglong save_utime_after_lock= thd->utime_after_lock; + DBUG_ENTER("open_performance_schema_table"); + + thd->reset_n_backup_open_tables_state(backup); + + if ((table= open_ltable(thd, one_table, one_table->lock_type, flags))) + { + DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_PERFORMANCE); + /* Make sure all columns get assigned to a default value */ table->use_all_columns(); + table->no_replicate= 1; + /* + Don't set automatic timestamps as we may want to use time of logging, + not from query start + */ + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; } + else + thd->restore_backup_open_tables_state(backup); + thd->utime_after_lock= save_utime_after_lock; DBUG_RETURN(table); } + +/** + Close a performance schema table. + The last table opened by open_performance_schema_table() + is closed, then the thread context is restored. + @param thd The current thread + @param backup [in] the context to restore. +*/ +void close_performance_schema_table(THD *thd, Open_tables_state *backup) +{ + bool found_old_table; + + /* + If open_performance_schema_table() fails, + this function should not be called. + */ + DBUG_ASSERT(thd->lock != NULL); + + /* + Note: + We do not create explicitly a separate transaction for the + performance table I/O, but borrow the current transaction. + lock + unlock will autocommit the change done in the + performance schema table: this is the expected result. + The current transaction should not be affected by this code. + TODO: Note that if a transactional engine is used for log tables, + this code will need to be revised, as a separate transaction + might be needed. + */ + mysql_unlock_tables(thd, thd->lock); + thd->lock= 0; + + pthread_mutex_lock(&LOCK_open); + + found_old_table= false; + while (thd->open_tables) + found_old_table|= close_thread_table(thd, &thd->open_tables); + + if (found_old_table) + broadcast_refresh(); + + pthread_mutex_unlock(&LOCK_open); + + thd->restore_backup_open_tables_state(backup); +} + +/** + @} (end of group Data_Dictionary) +*/ diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc index 87224b8eea0..a3a804db8f0 100644 --- a/sql/sql_binlog.cc +++ b/sql/sql_binlog.cc @@ -52,7 +52,7 @@ void mysql_client_binlog_statement(THD* thd) Allocation */ if (!thd->rli_fake) - thd->rli_fake= new RELAY_LOG_INFO; + thd->rli_fake= new Relay_log_info; const Format_description_log_event *desc= new Format_description_log_event(4); diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 719c67aff6a..ada927fa467 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -268,6 +268,39 @@ are stored in one block. If join_results allocated new block(s) then we need call pack_cache again. +7. Interface +The query cache interfaces with the rest of the server code through 7 +functions: + 1. Query_cache::send_result_to_client + - Called before parsing and used to match a statement with the stored + queries hash. + If a match is found the cached result set is sent through repeated + calls to net_real_write. (note: calling thread doesn't have a regis- + tered result set writer: thd->net.query_cache_query=0) + 2. Query_cache::store_query + - Called just before handle_select() and is used to register a result + set writer to the statement currently being processed + (thd->net.query_cache_query). + 3. query_cache_insert + - Called from net_real_write to append a result set to a cached query + if (and only if) this query has a registered result set writer + (thd->net.query_cache_query). + 4. Query_cache::invalidate + - Called from various places to invalidate query cache based on data- + base, table and myisam file name. During an on going invalidation + the query cache is temporarily disabled. + 5. Query_cache::flush + - Used when a RESET QUERY CACHE is issued. This clears the entire + cache block by block. + 6. Query_cache::resize + - Used to change the available memory used by the query cache. This + will also invalidate the entrie query cache in one free operation. + 7. Query_cache::pack + - Used when a FLUSH QUERY CACHE is issued. This changes the order of + the used memory blocks in physical memory order and move all avail- + able memory to the 'bottom' of the memory. + + TODO list: - Delayed till after-parsing qache answer (for column rights processing) @@ -615,49 +648,55 @@ void query_cache_insert(NET *net, const char *packet, ulong length) DBUG_VOID_RETURN; STRUCT_LOCK(&query_cache.structure_guard_mutex); + bool interrupt; + query_cache.wait_while_table_flush_is_in_progress(&interrupt); + if (interrupt) + { + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + return; + } - if (unlikely(query_cache.query_cache_size == 0 || - query_cache.flush_in_progress)) + Query_cache_block *query_block= (Query_cache_block*)net->query_cache_query; + if (!query_block) { + /* + We lost the writer and the currently processed query has been + invalidated; there is nothing left to do. + */ STRUCT_UNLOCK(&query_cache.structure_guard_mutex); DBUG_VOID_RETURN; } - Query_cache_block *query_block = ((Query_cache_block*) - net->query_cache_query); - if (query_block) - { - Query_cache_query *header = query_block->query(); - Query_cache_block *result = header->result(); + Query_cache_query *header= query_block->query(); + Query_cache_block *result= header->result(); - DUMP(&query_cache); - BLOCK_LOCK_WR(query_block); - DBUG_PRINT("qcache", ("insert packet %lu bytes long",length)); + DUMP(&query_cache); + BLOCK_LOCK_WR(query_block); + DBUG_PRINT("qcache", ("insert packet %lu bytes long",length)); - /* - On success STRUCT_UNLOCK(&query_cache.structure_guard_mutex) will be - done by query_cache.append_result_data if success (if not we need - query_cache.structure_guard_mutex locked to free query) - */ - if (!query_cache.append_result_data(&result, length, (uchar*) packet, - query_block)) - { - DBUG_PRINT("warning", ("Can't append data")); - header->result(result); - DBUG_PRINT("qcache", ("free query 0x%lx", (ulong) query_block)); - // The following call will remove the lock on query_block - query_cache.free_query(query_block); - // append_result_data no success => we need unlock - STRUCT_UNLOCK(&query_cache.structure_guard_mutex); - DBUG_VOID_RETURN; - } + /* + On success, STRUCT_UNLOCK is done by append_result_data. Otherwise, we + still need structure_guard_mutex to free the query, and therefore unlock + it later in this function. + */ + if (!query_cache.append_result_data(&result, length, (uchar*) packet, + query_block)) + { + DBUG_PRINT("warning", ("Can't append data")); header->result(result); - header->last_pkt_nr= net->pkt_nr; - BLOCK_UNLOCK_WR(query_block); - DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0);); - } - else + DBUG_PRINT("qcache", ("free query 0x%lx", (ulong) query_block)); + // The following call will remove the lock on query_block + query_cache.free_query(query_block); + // append_result_data no success => we need unlock STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + DBUG_VOID_RETURN; + } + + header->result(result); + header->last_pkt_nr= net->pkt_nr; + BLOCK_UNLOCK_WR(query_block); + DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0);); + DBUG_VOID_RETURN; } @@ -671,17 +710,21 @@ void query_cache_abort(NET *net) DBUG_VOID_RETURN; STRUCT_LOCK(&query_cache.structure_guard_mutex); - - if (unlikely(query_cache.query_cache_size == 0 || - query_cache.flush_in_progress)) + bool interrupt; + query_cache.wait_while_table_flush_is_in_progress(&interrupt); + if (interrupt) { STRUCT_UNLOCK(&query_cache.structure_guard_mutex); DBUG_VOID_RETURN; } + /* + While we were waiting another thread might have changed the status + of the writer. Make sure the writer still exists before continue. + */ Query_cache_block *query_block= ((Query_cache_block*) net->query_cache_query); - if (query_block) // Test if changed by other thread + if (query_block) { DUMP(&query_cache); BLOCK_LOCK_WR(query_block); @@ -706,6 +749,12 @@ void query_cache_end_of_result(THD *thd) if (thd->net.query_cache_query == 0) DBUG_VOID_RETURN; + if (thd->killed) + { + query_cache_abort(&thd->net); + DBUG_VOID_RETURN; + } + #ifdef EMBEDDED_LIBRARY query_cache_insert(&thd->net, (char*)thd, emb_count_querycache_size(thd)); @@ -713,47 +762,62 @@ void query_cache_end_of_result(THD *thd) STRUCT_LOCK(&query_cache.structure_guard_mutex); - if (unlikely(query_cache.query_cache_size == 0 || - query_cache.flush_in_progress)) - goto end; + bool interrupt; + query_cache.wait_while_table_flush_is_in_progress(&interrupt); + if (interrupt) + { + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + DBUG_VOID_RETURN; + } query_block= ((Query_cache_block*) thd->net.query_cache_query); if (query_block) { + /* + The writer is still present; finish last result block by chopping it to + suitable size if needed and setting block type. Since this is the last + block, the writer should be dropped. + */ DUMP(&query_cache); BLOCK_LOCK_WR(query_block); Query_cache_query *header= query_block->query(); - Query_cache_block *last_result_block= header->result()->prev; - ulong allign_size= ALIGN_SIZE(last_result_block->used); - ulong len= max(query_cache.min_allocation_unit, allign_size); - if (last_result_block->length >= query_cache.min_allocation_unit + len) - query_cache.split_block(last_result_block,len); + Query_cache_block *last_result_block; + ulong allign_size; + ulong len; -#ifndef DBUG_OFF if (header->result() == 0) { - DBUG_PRINT("error", ("end of data whith no result. query '%s'", - header->query())); - query_cache.wreck(__LINE__, ""); - + DBUG_PRINT("error", ("End of data with no result blocks; " + "Query '%s' removed from cache.", header->query())); /* - 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). + Extra safety: empty result should not happen in the normal call + to this function. In the release version that query should be ignored + and removed from QC. */ - goto end; + DBUG_ASSERT(0); + query_cache.free_query(query_block); + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + DBUG_VOID_RETURN; } -#endif + + last_result_block= header->result()->prev; + allign_size= ALIGN_SIZE(last_result_block->used); + len= max(query_cache.min_allocation_unit, allign_size); + if (last_result_block->length >= query_cache.min_allocation_unit + len) + query_cache.split_block(last_result_block,len); + header->found_rows(current_thd->limit_found_rows); header->result()->type= Query_cache_block::RESULT; + + /* Drop the writer. */ header->writer(0); thd->net.query_cache_query= 0; + BLOCK_UNLOCK_WR(query_block); DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); } -end: STRUCT_UNLOCK(&query_cache.structure_guard_mutex); DBUG_VOID_RETURN; } @@ -765,6 +829,18 @@ void query_cache_invalidate_by_MyISAM_filename(const char *filename) } +/* + The following function forms part of the C plugin API +*/ +extern "C" +void mysql_query_cache_invalidate4(THD *thd, + const char *key, unsigned key_length, + int using_trx) +{ + query_cache.invalidate(thd, key, (uint32) key_length, (my_bool) using_trx); +} + + /***************************************************************************** Query_cache methods *****************************************************************************/ @@ -795,27 +871,28 @@ Query_cache::Query_cache(ulong query_cache_limit_arg, ulong Query_cache::resize(ulong query_cache_size_arg) { + ulong new_query_cache_size; DBUG_ENTER("Query_cache::resize"); DBUG_PRINT("qcache", ("from %lu to %lu",query_cache_size, query_cache_size_arg)); DBUG_ASSERT(initialized); STRUCT_LOCK(&structure_guard_mutex); - while (flush_in_progress) - pthread_cond_wait(&COND_flush_finished, &structure_guard_mutex); - flush_in_progress= TRUE; + while (is_flushing()) + pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex); + m_cache_status= Query_cache::FLUSH_IN_PROGRESS; STRUCT_UNLOCK(&structure_guard_mutex); free_cache(); query_cache_size= query_cache_size_arg; - ulong new_query_cache_size= init_cache(); - - DBUG_EXECUTE("check_querycache",check_integrity(0);); + new_query_cache_size= init_cache(); STRUCT_LOCK(&structure_guard_mutex); - flush_in_progress= FALSE; - pthread_cond_signal(&COND_flush_finished); + m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS; + pthread_cond_signal(&COND_cache_status_changed); + if (new_query_cache_size) + DBUG_EXECUTE("check_querycache",check_integrity(1);); STRUCT_UNLOCK(&structure_guard_mutex); DBUG_RETURN(new_query_cache_size); @@ -910,8 +987,13 @@ def_week_frmt: %lu", ha_release_temporary_latches(thd); STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size == 0 || flush_in_progress) + if (query_cache_size == 0 || is_flushing()) { + /* + A table- or a full flush operation can potentially take a long time to + finish. We choose not to wait for them and skip caching statements + instead. + */ STRUCT_UNLOCK(&structure_guard_mutex); DBUG_VOID_RETURN; } @@ -954,7 +1036,7 @@ def_week_frmt: %lu", Query_cache_block *query_block; query_block= write_block_data(tot_length, (uchar*) thd->query, ALIGN_SIZE(sizeof(Query_cache_query)), - Query_cache_block::QUERY, local_tables, 1); + Query_cache_block::QUERY, local_tables); if (query_block != 0) { DBUG_PRINT("qcache", ("query block 0x%lx allocated, %lu", @@ -1088,13 +1170,21 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) } STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size == 0 || flush_in_progress) + + if (query_cache_size == 0) + goto err_unlock; + + if (is_flushing()) { - DBUG_PRINT("qcache", ("query cache disabled")); + /* Return; Query cache is temporarily disabled while we flush. */ + DBUG_PRINT("qcache",("query cache disabled")); goto err_unlock; } - /* Check that we haven't forgot to reset the query cache variables */ + /* + Check that we haven't forgot to reset the query cache variables; + make sure there are no attached query cache writer to this thread. + */ DBUG_ASSERT(thd->net.query_cache_query == 0); Query_cache_block *query_block; @@ -1267,7 +1357,9 @@ def_week_frmt: %lu", ("Handler require invalidation queries of %s.%s %lu-%lu", table_list.db, table_list.alias, (ulong) engine_data, (ulong) table->engine_data())); - invalidate_table((uchar *) table->db(), table->key_length()); + invalidate_table_internal(thd, + (uchar *) table->db(), + table->key_length()); } else thd->lex->safe_to_cache_query= 0; // Don't try to cache this @@ -1330,32 +1422,26 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used, my_bool using_transactions) { DBUG_ENTER("Query_cache::invalidate (table list)"); - STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size > 0 && !flush_in_progress) - { - DUMP(this); - using_transactions= using_transactions && - (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); - for (; tables_used; tables_used= tables_used->next_local) - { - DBUG_ASSERT(!using_transactions || tables_used->table!=0); - if (tables_used->derived) - continue; - if (using_transactions && - (tables_used->table->file->table_cache_type() == - HA_CACHE_TBL_TRANSACT)) - /* - Tables_used->table can't be 0 in transaction. - Only 'drop' invalidate not opened table, but 'drop' - force transaction finish. - */ - thd->add_changed_table(tables_used->table); - else - invalidate_table(tables_used); - } + using_transactions= using_transactions && + (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); + for (; tables_used; tables_used= tables_used->next_local) + { + DBUG_ASSERT(!using_transactions || tables_used->table!=0); + if (tables_used->derived) + continue; + if (using_transactions && + (tables_used->table->file->table_cache_type() == + HA_CACHE_TBL_TRANSACT)) + /* + tables_used->table can't be 0 in transaction. + Only 'drop' invalidate not opened table, but 'drop' + force transaction finish. + */ + thd->add_changed_table(tables_used->table); + else + invalidate_table(thd, tables_used); } - STRUCT_UNLOCK(&structure_guard_mutex); DBUG_VOID_RETURN; } @@ -1363,21 +1449,13 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used, void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used) { DBUG_ENTER("Query_cache::invalidate (changed table list)"); - if (tables_used) + THD *thd= current_thd; + for (; tables_used; tables_used= tables_used->next) { - STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size > 0 && !flush_in_progress) - { - DUMP(this); - for (; tables_used; tables_used= tables_used->next) - { - invalidate_table((uchar*) tables_used->key, tables_used->key_length); - DBUG_PRINT("qcache", ("db: %s table: %s", tables_used->key, - tables_used->key+ - strlen(tables_used->key)+1)); - } - } - STRUCT_UNLOCK(&structure_guard_mutex); + invalidate_table(thd, (uchar*) tables_used->key, tables_used->key_length); + DBUG_PRINT("qcache", ("db: %s table: %s", tables_used->key, + tables_used->key+ + strlen(tables_used->key)+1)); } DBUG_VOID_RETURN; } @@ -1396,20 +1474,14 @@ void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used) void Query_cache::invalidate_locked_for_write(TABLE_LIST *tables_used) { DBUG_ENTER("Query_cache::invalidate_locked_for_write"); - if (tables_used) + for (; tables_used; tables_used= tables_used->next_local) { - STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size > 0 && !flush_in_progress) + if (tables_used->lock_type & (TL_WRITE_LOW_PRIORITY | TL_WRITE) && + tables_used->table) { - DUMP(this); - for (; tables_used; tables_used= tables_used->next_local) - { - if (tables_used->lock_type & (TL_WRITE_LOW_PRIORITY | TL_WRITE) && - tables_used->table) - invalidate_table(tables_used->table); - } + THD *thd= current_thd; + invalidate_table(thd, tables_used->table); } - STRUCT_UNLOCK(&structure_guard_mutex); } DBUG_VOID_RETURN; } @@ -1423,18 +1495,14 @@ void Query_cache::invalidate(THD *thd, TABLE *table, { DBUG_ENTER("Query_cache::invalidate (table)"); - STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size > 0 && !flush_in_progress) - { - using_transactions= using_transactions && - (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); - if (using_transactions && - (table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT)) - thd->add_changed_table(table); - else - invalidate_table(table); - } - STRUCT_UNLOCK(&structure_guard_mutex); + using_transactions= using_transactions && + (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); + if (using_transactions && + (table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT)) + thd->add_changed_table(table); + else + invalidate_table(thd, table); + DBUG_VOID_RETURN; } @@ -1443,31 +1511,80 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length, my_bool using_transactions) { DBUG_ENTER("Query_cache::invalidate (key)"); - - STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size > 0 && !flush_in_progress) - { - using_transactions= using_transactions && - (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); - if (using_transactions) // used for innodb => has_transactions() is TRUE - thd->add_changed_table(key, key_length); - else - invalidate_table((uchar*)key, key_length); - } - STRUCT_UNLOCK(&structure_guard_mutex); + + using_transactions= using_transactions && + (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); + if (using_transactions) // used for innodb => has_transactions() is TRUE + thd->add_changed_table(key, key_length); + else + invalidate_table(thd, (uchar*)key, key_length); DBUG_VOID_RETURN; } + +/** + @brief Synchronize the thread with any flushing operations. + + This helper function is called whenever a thread needs to operate on the + query cache structure (example: during invalidation). If a table flush is in + progress this function will wait for it to stop. If a full flush is in + progress, the function will set the interrupt parameter to indicate that the + current operation is redundant and should be interrupted. + + @param[out] interrupt This out-parameter will be set to TRUE if the calling + function is redundant and should be interrupted. + + @return If the interrupt-parameter is TRUE then m_cache_status is set to + NO_FLUSH_IN_PROGRESS. If the interrupt-parameter is FALSE then + m_cache_status is set to FLUSH_IN_PROGRESS. + The structure_guard_mutex will in any case be locked. +*/ + +void Query_cache::wait_while_table_flush_is_in_progress(bool *interrupt) +{ + while (is_flushing()) + { + /* + If there already is a full flush in progress query cache isn't enabled + and additional flushes are redundant; just return instead. + */ + if (m_cache_status == Query_cache::FLUSH_IN_PROGRESS) + { + *interrupt= TRUE; + return; + } + /* + If a table flush is in progress; wait on cache status to change. + */ + if (m_cache_status == Query_cache::TABLE_FLUSH_IN_PROGRESS) + pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex); + } + *interrupt= FALSE; +} + + /** @brief Remove all cached queries that uses the given database */ + void Query_cache::invalidate(char *db) { bool restart= FALSE; DBUG_ENTER("Query_cache::invalidate (db)"); + STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size > 0 && !flush_in_progress) + bool interrupt; + wait_while_table_flush_is_in_progress(&interrupt); + if (interrupt) + { + STRUCT_UNLOCK(&structure_guard_mutex); + return; + } + + THD *thd= current_thd; + + if (query_cache_size > 0) { if (tables_blocks) { @@ -1479,7 +1596,10 @@ void Query_cache::invalidate(char *db) Query_cache_block *next= table_block->next; Query_cache_table *table = table_block->table(); if (strcmp(table->db(),db) == 0) - invalidate_table(table_block); + { + Query_cache_block_table *list_root= table_block->table(0); + invalidate_query_block_list(thd,list_root); + } table_block= next; @@ -1526,21 +1646,12 @@ void Query_cache::invalidate_by_MyISAM_filename(const char *filename) { DBUG_ENTER("Query_cache::invalidate_by_MyISAM_filename"); - STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size > 0 && !flush_in_progress) - { - /* Calculate the key outside the lock to make the lock shorter */ - char key[MAX_DBKEY_LENGTH]; - uint32 db_length; - uint key_length= filename_2_table_key(key, filename, &db_length); - Query_cache_block *table_block; - if ((table_block = (Query_cache_block*) hash_search(&tables, - (uchar*) key, - key_length))) - invalidate_table(table_block); - } - STRUCT_UNLOCK(&structure_guard_mutex); - + /* Calculate the key outside the lock to make the lock shorter */ + char key[MAX_DBKEY_LENGTH]; + uint32 db_length; + uint key_length= filename_2_table_key(key, filename, &db_length); + THD *thd= current_thd; + invalidate_table(thd,(uchar *)key, key_length); DBUG_VOID_RETURN; } @@ -1562,16 +1673,43 @@ void Query_cache::flush() DBUG_VOID_RETURN; } - /* Join result in cache in 1 block (if result length > join_limit) */ + +/** + @brief Rearrange the memory blocks and join result in cache in 1 block (if + result length > join_limit) + + @param[in] join_limit If the minimum length of a result block to be joined. + @param[in] iteration_limit The maximum number of packing and joining + sequences. + +*/ void Query_cache::pack(ulong join_limit, uint iteration_limit) { DBUG_ENTER("Query_cache::pack"); + + bool interrupt; + STRUCT_LOCK(&structure_guard_mutex); + wait_while_table_flush_is_in_progress(&interrupt); + if (interrupt) + { + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_VOID_RETURN; + } + + if (query_cache_size == 0) + { + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_VOID_RETURN; + } + uint i = 0; do { pack_cache(); } while ((++i < iteration_limit) && join_results(join_limit)); + + STRUCT_UNLOCK(&structure_guard_mutex); DBUG_VOID_RETURN; } @@ -1590,7 +1728,7 @@ void Query_cache::destroy() free_cache(); STRUCT_UNLOCK(&structure_guard_mutex); - pthread_cond_destroy(&COND_flush_finished); + pthread_cond_destroy(&COND_cache_status_changed); pthread_mutex_destroy(&structure_guard_mutex); initialized = 0; } @@ -1606,8 +1744,8 @@ void Query_cache::init() { DBUG_ENTER("Query_cache::init"); pthread_mutex_init(&structure_guard_mutex,MY_MUTEX_INIT_FAST); - pthread_cond_init(&COND_flush_finished, NULL); - flush_in_progress= FALSE; + pthread_cond_init(&COND_cache_status_changed, NULL); + m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS; initialized = 1; DBUG_VOID_RETURN; } @@ -1809,9 +1947,10 @@ void Query_cache::make_disabled() /** @class Query_cache @brief Free all resources allocated by the cache. - @details This function frees all resources allocated by the cache. You - have to call init_cache() before using the cache again. This function requires - the structure_guard_mutex to be locked. + + This function frees all resources allocated by the cache. You + have to call init_cache() before using the cache again. This function + requires the structure_guard_mutex to be locked. */ void Query_cache::free_cache() @@ -1830,24 +1969,17 @@ void Query_cache::free_cache() *****************************************************************************/ -/* - flush_cache() - flush the cache. - - SYNOPSIS - flush_cache() - - DESCRIPTION - This function will flush cache contents. It assumes we have - 'structure_guard_mutex' locked. The function sets the - 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. +/** + @brief Flush the cache. + + This function will flush cache contents. It assumes we have + 'structure_guard_mutex' locked. The function sets the m_cache_status 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() @@ -1859,15 +1991,15 @@ void Query_cache::flush_cache() Query_cache::free_cache()) depends on the fact that after the flush the cache is empty. */ - while (flush_in_progress) - pthread_cond_wait(&COND_flush_finished, &structure_guard_mutex); + while (is_flushing()) + pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex); /* - Setting 'flush_in_progress' will prevent other threads from using + Setting 'FLUSH_IN_PROGRESS' will prevent other threads from using the cache while we are in the middle of the flush, and we release the lock so that other threads won't block. */ - flush_in_progress= TRUE; + m_cache_status= Query_cache::FLUSH_IN_PROGRESS; STRUCT_UNLOCK(&structure_guard_mutex); my_hash_reset(&queries); @@ -1878,8 +2010,8 @@ void Query_cache::flush_cache() } STRUCT_LOCK(&structure_guard_mutex); - flush_in_progress= FALSE; - pthread_cond_signal(&COND_flush_finished); + m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS; + pthread_cond_signal(&COND_cache_status_changed); } /* @@ -1897,7 +2029,7 @@ my_bool Query_cache::free_old_query() sequence is breached. Also we don't need remove locked queries at this point. */ - Query_cache_block *query_block = 0; + Query_cache_block *query_block= 0; if (queries_blocks != 0) { Query_cache_block *block = queries_blocks; @@ -2035,8 +2167,7 @@ Query_cache_block * Query_cache::write_block_data(ulong data_len, uchar* data, ulong header_len, Query_cache_block::block_type type, - TABLE_COUNTER_TYPE ntab, - my_bool under_guard) + TABLE_COUNTER_TYPE ntab) { ulong all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) + ALIGN_SIZE(ntab*sizeof(Query_cache_block_table)) + @@ -2046,9 +2177,8 @@ Query_cache::write_block_data(ulong data_len, uchar* data, DBUG_ENTER("Query_cache::write_block_data"); DBUG_PRINT("qcache", ("data: %ld, header: %ld, all header: %ld", data_len, header_len, all_headers_len)); - Query_cache_block *block = allocate_block(max(align_len, - min_allocation_unit), - 1, 0, under_guard); + Query_cache_block *block= allocate_block(max(align_len, + min_allocation_unit),1, 0); if (block != 0) { block->type = type; @@ -2265,8 +2395,7 @@ my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block, if (!(new_block= allocate_block(max(min_size, align_len), min_result_data_size == 0, - all_headers_len + min_result_data_size, - 1))) + all_headers_len + min_result_data_size))) { DBUG_PRINT("warning", ("Can't allocate block for results")); DBUG_RETURN(FALSE); @@ -2308,51 +2437,109 @@ my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block, Invalidate the first table in the table_list */ -void Query_cache::invalidate_table(TABLE_LIST *table_list) +void Query_cache::invalidate_table(THD *thd, TABLE_LIST *table_list) { if (table_list->table != 0) - invalidate_table(table_list->table); // Table is open + invalidate_table(thd, table_list->table); // Table is open else { char key[MAX_DBKEY_LENGTH]; uint key_length; - Query_cache_block *table_block; + key_length=(uint) (strmov(strmov(key,table_list->db)+1, table_list->table_name) -key)+ 1; // We don't store temporary tables => no key_length+=4 ... - if ((table_block = (Query_cache_block*) - hash_search(&tables,(uchar*) key,key_length))) - invalidate_table(table_block); + invalidate_table(thd, (uchar *)key, key_length); } } -void Query_cache::invalidate_table(TABLE *table) +void Query_cache::invalidate_table(THD *thd, TABLE *table) { - invalidate_table((uchar*) table->s->table_cache_key.str, + invalidate_table(thd, (uchar*) table->s->table_cache_key.str, table->s->table_cache_key.length); } -void Query_cache::invalidate_table(uchar * key, uint32 key_length) +void Query_cache::invalidate_table(THD *thd, uchar * key, uint32 key_length) +{ + bool interrupt; + STRUCT_LOCK(&structure_guard_mutex); + wait_while_table_flush_is_in_progress(&interrupt); + if (interrupt) + { + STRUCT_UNLOCK(&structure_guard_mutex); + return; + } + + /* + Setting 'TABLE_FLUSH_IN_PROGRESS' will temporarily disable the cache + so that structural changes to cache won't block the entire server. + However, threads requesting to change the query cache will still have + to wait for the flush to finish. + */ + m_cache_status= Query_cache::TABLE_FLUSH_IN_PROGRESS; + STRUCT_UNLOCK(&structure_guard_mutex); + + if (query_cache_size > 0) + invalidate_table_internal(thd, key, key_length); + + STRUCT_LOCK(&structure_guard_mutex); + m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS; + + /* + net_real_write might be waiting on a change on the m_cache_status + variable. + */ + pthread_cond_signal(&COND_cache_status_changed); + STRUCT_UNLOCK(&structure_guard_mutex); +} + + +/** + Try to locate and invalidate a table by name. + The caller must ensure that no other thread is trying to work with + the query cache when this function is executed. + + @pre structure_guard_mutex is acquired or TABLE_FLUSH_IN_PROGRESS is set. +*/ + +void +Query_cache::invalidate_table_internal(THD *thd, uchar *key, uint32 key_length) { - Query_cache_block *table_block; - if ((table_block = ((Query_cache_block*) - hash_search(&tables, key, key_length)))) - invalidate_table(table_block); + Query_cache_block *table_block= + (Query_cache_block*)hash_search(&tables, key, key_length); + if (table_block) + { + Query_cache_block_table *list_root= table_block->table(0); + invalidate_query_block_list(thd, list_root); + } } -void Query_cache::invalidate_table(Query_cache_block *table_block) +/** + @brief Invalidate a linked list of query cache blocks. + + Each block tries to aquire a block level lock before + free_query is a called. This function will in turn affect + related table- and result-blocks. + + @param[in,out] thd Thread context. + @param[in,out] list_root A pointer to a circular list of query blocks. + +*/ + +void +Query_cache::invalidate_query_block_list(THD *thd, + Query_cache_block_table *list_root) { - Query_cache_block_table *list_root = table_block->table(0); while (list_root->next != list_root) { - Query_cache_block *query_block = list_root->next->block(); + Query_cache_block *query_block= list_root->next->block(); BLOCK_LOCK_WR(query_block); free_query(query_block); + DBUG_EXECUTE_IF("debug_cache_locks", sleep(10);); } } - /* Register given table list begining with given position in tables table of block @@ -2495,9 +2682,13 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block, return (n); } -/* - Insert used tablename in cache - Returns 0 on error + +/** + @brief Insert used table name into the cache. + + @return Error status + @retval FALSE On error + @retval TRUE On success */ my_bool @@ -2511,9 +2702,10 @@ Query_cache::insert_table(uint key_len, char *key, DBUG_PRINT("qcache", ("insert table node 0x%lx, len %d", (ulong)node, key_len)); - Query_cache_block *table_block = ((Query_cache_block *) - hash_search(&tables, (uchar*) key, - key_len)); + THD *thd= current_thd; + + Query_cache_block *table_block= + (Query_cache_block *)hash_search(&tables, (uchar*) key, key_len); if (table_block && table_block->table()->engine_data() != engine_data) @@ -2528,7 +2720,11 @@ Query_cache::insert_table(uint key_len, char *key, as far as we delete all queries with this table, table block will be deleted, too */ - invalidate_table(table_block); + { + Query_cache_block_table *list_root= table_block->table(0); + invalidate_query_block_list(thd, list_root); + } + table_block= 0; } @@ -2536,21 +2732,29 @@ Query_cache::insert_table(uint key_len, char *key, { DBUG_PRINT("qcache", ("new table block from 0x%lx (%u)", (ulong) key, (int) key_len)); - table_block = write_block_data(key_len, (uchar*) key, - ALIGN_SIZE(sizeof(Query_cache_table)), - Query_cache_block::TABLE, - 1, 1); + table_block= write_block_data(key_len, (uchar*) key, + ALIGN_SIZE(sizeof(Query_cache_table)), + Query_cache_block::TABLE, 1); if (table_block == 0) { DBUG_PRINT("qcache", ("Can't write table name to cache")); DBUG_RETURN(0); } - Query_cache_table *header = table_block->table(); + Query_cache_table *header= table_block->table(); double_linked_list_simple_include(table_block, - &tables_blocks); - Query_cache_block_table *list_root = table_block->table(0); - list_root->n = 0; - list_root->next = list_root->prev = list_root; + &tables_blocks); + /* + First node in the Query_cache_block_table-chain is the table-type + block. This block will only have one Query_cache_block_table (n=0). + */ + Query_cache_block_table *list_root= table_block->table(0); + list_root->n= 0; + + /* + The node list is circular in nature. + */ + list_root->next= list_root->prev= list_root; + if (my_hash_insert(&tables, (const uchar *) table_block)) { DBUG_PRINT("qcache", ("Can't insert table to hash")); @@ -2558,20 +2762,37 @@ Query_cache::insert_table(uint key_len, char *key, free_memory_block(table_block); DBUG_RETURN(0); } - char *db = header->db(); + char *db= header->db(); header->table(db + db_length + 1); header->key_length(key_len); header->type(cache_type); header->callback(callback); header->engine_data(engine_data); + + /* + We insert this table without the assumption that it isn't refrenenced by + any queries. + */ + header->m_cached_query_count= 0; } - Query_cache_block_table *list_root = table_block->table(0); - node->next = list_root->next; - list_root->next = node; - node->next->prev = node; - node->prev = list_root; - node->parent = table_block->table(); + /* + Table is now in the cache; link the table_block-node associated + with the currently processed query into the chain of queries depending + on the cached table. + */ + Query_cache_block_table *list_root= table_block->table(0); + node->next= list_root->next; + list_root->next= node; + node->next->prev= node; + node->prev= list_root; + node->parent= table_block->table(); + /* + Increase the counter to keep track on how long this chain + of queries is. + */ + Query_cache_table *table_block_data= table_block->table(); + table_block_data->m_cached_query_count++; DBUG_RETURN(1); } @@ -2579,15 +2800,27 @@ Query_cache::insert_table(uint key_len, char *key, void Query_cache::unlink_table(Query_cache_block_table *node) { DBUG_ENTER("Query_cache::unlink_table"); - node->prev->next = node->next; - node->next->prev = node->prev; - Query_cache_block_table *neighbour = node->next; + node->prev->next= node->next; + node->next->prev= node->prev; + Query_cache_block_table *neighbour= node->next; + Query_cache_table *table_block_data= node->parent; + table_block_data->m_cached_query_count--; + + DBUG_ASSERT(table_block_data->m_cached_query_count >= 0); + if (neighbour->next == neighbour) { - // list is empty (neighbor is root of list) - Query_cache_block *table_block = neighbour->block(); + DBUG_ASSERT(table_block_data->m_cached_query_count == 0); + /* + If neighbor is root of list, the list is empty. + The root of the list is always a table-type block + which contain exactly one Query_cache_block_table + node object, thus we can use the block() method + to calculate the Query_cache_block address. + */ + Query_cache_block *table_block= neighbour->block(); double_linked_list_exclude(table_block, - &tables_blocks); + &tables_blocks); hash_delete(&tables,(uchar *) table_block); free_memory_block(table_block); } @@ -2599,12 +2832,11 @@ void Query_cache::unlink_table(Query_cache_block_table *node) *****************************************************************************/ Query_cache_block * -Query_cache::allocate_block(ulong len, my_bool not_less, ulong min, - my_bool under_guard) +Query_cache::allocate_block(ulong len, my_bool not_less, ulong min) { DBUG_ENTER("Query_cache::allocate_block"); - DBUG_PRINT("qcache", ("len %lu, not less %d, min %lu, uder_guard %d", - len, not_less,min,under_guard)); + DBUG_PRINT("qcache", ("len %lu, not less %d, min %lu", + len, not_less,min)); if (len >= min(query_cache_size, query_cache_limit)) { @@ -2613,17 +2845,6 @@ Query_cache::allocate_block(ulong len, my_bool not_less, ulong min, DBUG_RETURN(0); // in any case we don't have such piece of memory } - if (!under_guard) - { - STRUCT_LOCK(&structure_guard_mutex); - - if (unlikely(query_cache.query_cache_size == 0 || flush_in_progress)) - { - STRUCT_UNLOCK(&structure_guard_mutex); - DBUG_RETURN(0); - } - } - /* Free old queries until we have enough memory to store this block */ Query_cache_block *block; do @@ -2638,8 +2859,6 @@ Query_cache::allocate_block(ulong len, my_bool not_less, ulong min, split_block(block,ALIGN_SIZE(len)); } - if (!under_guard) - STRUCT_UNLOCK(&structure_guard_mutex); DBUG_RETURN(block); } @@ -2874,9 +3093,7 @@ uint Query_cache::find_bin(ulong size) } uint bin = steps[left].idx - (uint)((size - steps[left].size)/steps[left].increment); -#ifndef DBUG_OFF - bins_dump(); -#endif + DBUG_PRINT("qcache", ("bin %u step %u, size %lu step size %lu", bin, left, size, steps[left].size)); DBUG_RETURN(bin); @@ -3014,14 +3231,42 @@ void Query_cache::double_linked_list_join(Query_cache_block *head_tail, >0 number of tables */ -static TABLE_COUNTER_TYPE process_and_count_tables(TABLE_LIST *tables_used, - uint8 *tables_type) +TABLE_COUNTER_TYPE +Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used, + uint8 *tables_type) { DBUG_ENTER("process_and_count_tables"); TABLE_COUNTER_TYPE table_count = 0; for (; tables_used; tables_used= tables_used->next_global) { table_count++; +#ifdef HAVE_QUERY_CACHE + /* + Disable any attempt to store this statement if there are + column level grants on any referenced tables. + The grant.want_privileges flag was set to 1 in the + check_grant() function earlier if the TABLE_LIST object + had any associated column privileges. + + We need to check that the TABLE_LIST object isn't part + of a VIEW definition because we want to be able to cache + views. + + TODO: Although it is possible to cache views, the privilege + check on view tables always fall back on column privileges + even if there are more generic table privileges. Thus it isn't + currently possible to retrieve cached view-tables unless the + client has the super user privileges. + */ + if (tables_used->grant.want_privilege && + tables_used->belong_to_view == NULL) + { + DBUG_PRINT("qcache", ("Don't cache statement as it refers to " + "tables with column privileges.")); + thd->lex->safe_to_cache_query= 0; + DBUG_RETURN(0); + } +#endif if (tables_used->view) { DBUG_PRINT("qcache", ("view: %s db: %s", @@ -3099,7 +3344,8 @@ Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex, (long) lex->select_lex.options, (int) thd->variables.query_cache_type)); - if (!(table_count= process_and_count_tables(tables_used, tables_type))) + if (!(table_count= process_and_count_tables(thd, tables_used, + tables_type))) DBUG_RETURN(0); if ((thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) && @@ -3165,18 +3411,17 @@ my_bool Query_cache::ask_handler_allowance(THD *thd, Packing *****************************************************************************/ + +/** + @brief Rearrange all memory blocks so that free memory joins at the + 'bottom' of the allocated memory block containing all cache data. + @see Query_cache::pack(ulong join_limit, uint iteration_limit) +*/ + void Query_cache::pack_cache() { DBUG_ENTER("Query_cache::pack_cache"); - STRUCT_LOCK(&structure_guard_mutex); - - if (unlikely(query_cache_size == 0 || flush_in_progress)) - { - STRUCT_UNLOCK(&structure_guard_mutex); - DBUG_VOID_RETURN; - } - DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); uchar *border = 0; @@ -3210,7 +3455,6 @@ void Query_cache::pack_cache() } DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); - STRUCT_UNLOCK(&structure_guard_mutex); DBUG_VOID_RETURN; } @@ -3485,8 +3729,7 @@ my_bool Query_cache::join_results(ulong join_limit) my_bool has_moving = 0; DBUG_ENTER("Query_cache::join_results"); - STRUCT_LOCK(&structure_guard_mutex); - if (queries_blocks != 0 && !flush_in_progress) + if (queries_blocks != 0) { DBUG_ASSERT(query_cache_size > 0); Query_cache_block *block = queries_blocks; @@ -3549,7 +3792,6 @@ my_bool Query_cache::join_results(ulong join_limit) block = block->next; } while ( block != queries_blocks ); } - STRUCT_UNLOCK(&structure_guard_mutex); DBUG_RETURN(has_moving); } @@ -3785,6 +4027,14 @@ void Query_cache::tables_dump() } +/** + @brief Checks integrity of the various linked lists + + @return Error status code + @retval FALSE Query cache is operational. + @retval TRUE Query cache is broken. +*/ + my_bool Query_cache::check_integrity(bool locked) { my_bool result = 0; @@ -3794,14 +4044,8 @@ my_bool Query_cache::check_integrity(bool locked) if (!locked) STRUCT_LOCK(&structure_guard_mutex); - if (unlikely(query_cache_size == 0 || flush_in_progress)) - { - if (!locked) - STRUCT_UNLOCK(&query_cache.structure_guard_mutex); - - DBUG_PRINT("qcache", ("Query Cache not initialized")); - DBUG_RETURN(0); - } + while (is_flushing()) + pthread_cond_wait(&COND_cache_status_changed,&structure_guard_mutex); if (hash_check(&queries)) { @@ -3820,6 +4064,10 @@ my_bool Query_cache::check_integrity(bool locked) Query_cache_block * block = first_block; do { + /* When checking at system start, there is no block. */ + if (!block) + break; + DBUG_PRINT("qcache", ("block 0x%lx, type %u...", (ulong) block, (uint) block->type)); // Check allignment diff --git a/sql/sql_cache.h b/sql/sql_cache.h index 3c5d784ce94..b47e2e9a43b 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -65,17 +65,44 @@ struct Query_cache_query; struct Query_cache_result; class Query_cache; +/** + @brief This class represents a node in the linked chain of queries + belonging to one table. + @note The root of this linked list is not a query-type block, but the table- + type block which all queries has in common. +*/ struct Query_cache_block_table { Query_cache_block_table() {} /* Remove gcc warning */ - TABLE_COUNTER_TYPE n; // numbr in table (from 0) + + /** + This node holds a position in a static table list belonging + to the associated query (base 0). + */ + TABLE_COUNTER_TYPE n; + + /** + Pointers to the next and previous node, linking all queries with + a common table. + */ Query_cache_block_table *next, *prev; + + /** + A pointer to the table-type block which all + linked queries has in common. + */ Query_cache_table *parent; + + /** + A method to calculate the address of the query cache block + owning this node. The purpose of this calculation is to + make it easier to move the query cache block without having + to modify all the pointer addresses. + */ inline Query_cache_block *block(); }; - struct Query_cache_block { Query_cache_block() {} /* Remove gcc warning */ @@ -115,6 +142,7 @@ struct Query_cache_query uint8 tbls_type; unsigned int last_pkt_nr; + Query_cache_query() {} /* Remove gcc warning */ inline void init_n_lock(); void unlock_n_destroy(); inline ulonglong found_rows() { return limit_found_rows; } @@ -151,6 +179,11 @@ struct Query_cache_table /* data need by some engines */ ulonglong engine_data_buff; + /** + The number of queries depending of this table. + */ + int32 m_cached_query_count; + inline char *db() { return (char *) data(); } inline char *table() { return tbl; } inline void table(char *table_arg) { tbl= table_arg; } @@ -237,11 +270,17 @@ public: ulong free_memory, queries_in_cache, hits, inserts, refused, free_memory_blocks, total_blocks, lowmem_prunes; + private: - pthread_cond_t COND_flush_finished; - bool flush_in_progress; + pthread_cond_t COND_cache_status_changed; + + enum Cache_status { NO_FLUSH_IN_PROGRESS, FLUSH_IN_PROGRESS, + TABLE_FLUSH_IN_PROGRESS }; + + Cache_status m_cache_status; void free_query_internal(Query_cache_block *point); + void invalidate_table_internal(THD *thd, uchar *key, uint32 key_length); protected: /* @@ -253,7 +292,7 @@ protected: 2. query block (for operation inside query (query block/results)) Thread doing cache flush releases the mutex once it sets - flush_in_progress flag, so other threads may bypass the cache as + m_cache_status flag, so other threads may bypass the cache as if it is disabled, not waiting for reset to finish. The exception is other threads that were going to do cache flush---they'll wait till the end of a flush operation. @@ -270,6 +309,7 @@ protected: /* options */ ulong min_allocation_unit, min_result_data_size; uint def_query_hash_size, def_table_hash_size; + uint mem_bin_num, mem_bin_steps; // See at init_cache & find_bin my_bool initialized; @@ -295,10 +335,13 @@ protected: ulong data_len, Query_cache_block *query_block, my_bool first_block); - void invalidate_table(TABLE_LIST *table); - void invalidate_table(TABLE *table); - void invalidate_table(uchar *key, uint32 key_length); - void invalidate_table(Query_cache_block *table_block); + void invalidate_table(THD *thd, TABLE_LIST *table); + void invalidate_table(THD *thd, TABLE *table); + void invalidate_table(THD *thd, uchar *key, uint32 key_length); + void invalidate_table(THD *thd, Query_cache_block *table_block); + void invalidate_query_block_list(THD *thd, + Query_cache_block_table *list_root); + TABLE_COUNTER_TYPE register_tables_from_list(TABLE_LIST *tables_used, TABLE_COUNTER_TYPE counter, @@ -337,6 +380,8 @@ protected: Query_cache_block *pprev); my_bool join_results(ulong join_limit); + void wait_while_table_flush_is_in_progress(bool *interrupt); + /* Following function control structure_guard_mutex by themself or don't need structure_guard_mutex @@ -347,8 +392,7 @@ protected: Query_cache_block *write_block_data(ulong data_len, uchar* data, ulong header_len, Query_cache_block::block_type type, - TABLE_COUNTER_TYPE ntab = 0, - my_bool under_guard=0); + TABLE_COUNTER_TYPE ntab = 0); my_bool append_result_data(Query_cache_block **result, ulong data_len, uchar* data, Query_cache_block *parent); @@ -360,16 +404,17 @@ protected: inline ulong get_min_first_result_data_size(); inline ulong get_min_append_result_data_size(); Query_cache_block *allocate_block(ulong len, my_bool not_less, - ulong min, - my_bool under_guard=0); + ulong min); /* If query is cacheable return number tables in query (query without tables not cached) */ - static TABLE_COUNTER_TYPE is_cacheable(THD *thd, uint32 query_len, char *query, - LEX *lex, TABLE_LIST *tables_used, - uint8 *tables_type); + LEX *lex, TABLE_LIST *tables_used, + uint8 *tables_type); + TABLE_COUNTER_TYPE process_and_count_tables(THD *thd, + TABLE_LIST *tables_used, + uint8 *tables_type); static my_bool ask_handler_allowance(THD *thd, TABLE_LIST *tables_used); public: @@ -424,6 +469,11 @@ protected: friend void query_cache_end_of_result(THD *thd); friend void query_cache_abort(NET *net); + bool is_flushing(void) + { + return (m_cache_status != Query_cache::NO_FLUSH_IN_PROGRESS); + } + /* The following functions are only used when debugging We don't protect these with ifndef DBUG_OFF to not have to recompile diff --git a/sql/sql_class.cc b/sql/sql_class.cc index d83b3f51ce0..df134f82217 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -207,6 +207,31 @@ Open_tables_state::Open_tables_state(ulong version_arg) The following functions form part of the C plugin API */ +extern "C" int mysql_tmpfile(const char *prefix) +{ + char filename[FN_REFLEN]; + File fd = create_temp_file(filename, mysql_tmpdir, prefix, +#ifdef __WIN__ + O_BINARY | O_TRUNC | O_SEQUENTIAL | + O_SHORT_LIVED | +#endif /* __WIN__ */ + O_CREAT | O_EXCL | O_RDWR | O_TEMPORARY, + MYF(MY_WME)); + if (fd >= 0) { +#ifndef __WIN__ + /* + This can be removed once the following bug is fixed: + Bug #28903 create_temp_file() doesn't honor O_TEMPORARY option + (file not removed) (Unix) + */ + unlink(filename); +#endif /* !__WIN__ */ + } + + return fd; +} + + extern "C" int thd_in_lock_tables(const THD *thd) { @@ -253,6 +278,11 @@ int thd_tx_isolation(const THD *thd) return (int) thd->variables.tx_isolation; } +extern "C" +void thd_inc_row_count(THD *thd) +{ + thd->row_count++; +} /* Dumps a text description of a thread, its security context @@ -337,6 +367,8 @@ THD::THD() stmt_depends_on_first_successful_insert_id_in_prev_stmt(FALSE), global_read_lock(0), is_fatal_error(0), + transaction_rollback_request(0), + is_fatal_sub_stmt_error(0), rand_used(0), time_zone_used(0), in_lock_tables(0), @@ -355,7 +387,6 @@ THD::THD() init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0); stmt_arena= this; thread_stack= 0; - db= 0; catalog= (char*)"std"; // the only catalog we have for now main_security_ctx.init(); security_ctx= &main_security_ctx; @@ -363,13 +394,14 @@ THD::THD() query_start_used= 0; count_cuted_fields= CHECK_FIELD_IGNORE; killed= NOT_KILLED; - db_length= col_access=0; - query_error= tmp_table_used= 0; + col_access=0; + query_error= thread_specific_used= FALSE; hash_clear(&handler_tables_hash); tmp_table=0; used_tables=0; cuted_fields= sent_row_count= row_count= 0L; limit_found_rows= 0; + row_count_func= -1; statement_id_counter= 0UL; #ifdef ERROR_INJECT_SUPPORT error_inject_value= 0UL; @@ -377,11 +409,12 @@ 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; + start_utime= 0L; + utime_after_lock= 0L; current_linfo = 0; slave_thread = 0; bzero(&variables, sizeof(variables)); - thread_id= variables.pseudo_thread_id= 0; + thread_id= 0; one_shot_set= 0; file_id = 0; query_id= 0; @@ -484,6 +517,49 @@ void THD::pop_internal_handler() m_internal_handler= NULL; } +extern "C" +void *thd_alloc(MYSQL_THD thd, unsigned int size) +{ + return thd->alloc(size); +} + +extern "C" +void *thd_calloc(MYSQL_THD thd, unsigned int size) +{ + return thd->calloc(size); +} + +extern "C" +char *thd_strdup(MYSQL_THD thd, const char *str) +{ + return thd->strdup(str); +} + +extern "C" +char *thd_strmake(MYSQL_THD thd, const char *str, unsigned int size) +{ + return thd->strmake(str, size); +} + +extern "C" +LEX_STRING *thd_make_lex_string(THD *thd, LEX_STRING *lex_str, + const char *str, unsigned int size, + int allocate_lex_string) +{ + return thd->make_lex_string(lex_str, str, size, + (bool) allocate_lex_string); +} + +extern "C" +void *thd_memdup(MYSQL_THD thd, const void* str, unsigned int size) +{ + return thd->memdup(str, size); +} + +void thd_get_xid(const MYSQL_THD thd, MYSQL_XID *xid) +{ + *xid = *(MYSQL_XID *) &thd->transaction.xid_state.xid; +} /* Init common variables that has to be reset on start and on change_user @@ -499,12 +575,18 @@ void THD::init(void) variables.date_format); variables.datetime_format= date_time_format_copy((THD*) 0, variables.datetime_format); + /* + variables= global_system_variables above has reset + variables.pseudo_thread_id to 0. We need to correct it here to + avoid temporary tables replication failure. + */ + variables.pseudo_thread_id= thread_id; pthread_mutex_unlock(&LOCK_global_system_variables); server_status= SERVER_STATUS_AUTOCOMMIT; if (variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) server_status|= SERVER_STATUS_NO_BACKSLASH_ESCAPES; options= thd_startup_options; - no_trans_update.stmt= no_trans_update.all= FALSE; + transaction.all.modified_non_trans_table= transaction.stmt.modified_non_trans_table= FALSE; open_options=ha_open_options; update_lock_default= (variables.low_priority_updates ? TL_WRITE_LOW_PRIORITY : @@ -852,6 +934,30 @@ void THD::cleanup_after_query() } +/** + Create a LEX_STRING in this connection + + @param lex_str pointer to LEX_STRING object to be initialized + @param str initializer to be copied into lex_str + @param length length of str, in bytes + @param allocate_lex_string if TRUE, allocate new LEX_STRING object, + instead of using lex_str value + @return NULL on failure, or pointer to the LEX_STRING object +*/ +LEX_STRING *THD::make_lex_string(LEX_STRING *lex_str, + const char* str, uint length, + bool allocate_lex_string) +{ + if (allocate_lex_string) + if (!(lex_str= (LEX_STRING *)alloc(sizeof(LEX_STRING)))) + return 0; + if (!(lex_str->str= strmake_root(mem_root, str, length))) + return 0; + lex_str->length= length; + return lex_str; +} + + /* Convert a string to another character set @@ -1201,7 +1307,7 @@ void select_send::abort() { DBUG_ENTER("select_send::abort"); if (status && thd->spcont && - thd->spcont->find_handler(thd->net.last_errno, + thd->spcont->find_handler(thd, thd->net.last_errno, MYSQL_ERROR::WARN_LEVEL_ERROR)) { /* @@ -1446,6 +1552,8 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u) field_sep_char= (exchange->enclosed->length() ? (*exchange->enclosed)[0] : field_term_length ? (*exchange->field_term)[0] : INT_MAX); escape_char= (exchange->escaped->length() ? (*exchange->escaped)[0] : -1); + is_ambiguous_field_sep= test(strchr(ESCAPE_CHARS, field_sep_char)); + is_unsafe_field_sep= test(strchr(NUMERIC_CHARS, field_sep_char)); line_sep_char= (exchange->line_term->length() ? (*exchange->line_term)[0] : INT_MAX); if (!field_term_length) @@ -1520,7 +1628,8 @@ bool select_export::send_data(List<Item> &items) used_length=min(res->length(),item->max_length); else used_length=res->length(); - if (result_type == STRING_RESULT && escape_char != -1) + if ((result_type == STRING_RESULT || is_unsafe_field_sep) && + escape_char != -1) { char *pos, *start, *end; CHARSET_INFO *res_charset= res->charset(); @@ -1586,7 +1695,9 @@ bool select_export::send_data(List<Item> &items) NEED_ESCAPING(pos[1]))) { char tmp_buff[2]; - tmp_buff[0]= escape_char; + tmp_buff[0]= ((int) *pos == field_sep_char && + is_ambiguous_field_sep) ? + field_sep_char : escape_char; tmp_buff[1]= *pos ? *pos : '0'; if (my_b_write(&cache,(uchar*) start,(uint) (pos-start)) || my_b_write(&cache,(uchar*) tmp_buff,2)) @@ -1929,7 +2040,9 @@ Statement::Statement(LEX *lex_arg, MEM_ROOT *mem_root_arg, lex(lex_arg), query(0), query_length(0), - cursor(0) + cursor(0), + db(NULL), + db_length(0) { name.str= NULL; } @@ -2312,6 +2425,10 @@ bool Security_context::set_user(char *user_arg) Initialize this security context from the passed in credentials and activate it in the current thread. + @param thd + @param definer_user + @param definer_host + @param db @param[out] backup Save a pointer to the current security context in the thread. In case of success it points to the saved old context, otherwise it points to NULL. @@ -2437,7 +2554,48 @@ void THD::restore_backup_open_tables_state(Open_tables_state *backup) DBUG_VOID_RETURN; } +/** + Check the killed state of a user thread + @param thd user thread + @retval 0 the user thread is active + @retval 1 the user thread has been killed +*/ +extern "C" int thd_killed(const MYSQL_THD thd) +{ + return(thd->killed); +} +#ifdef INNODB_COMPATIBILITY_HOOKS +extern "C" struct charset_info_st *thd_charset(MYSQL_THD thd) +{ + return(thd->charset()); +} + +extern "C" char **thd_query(MYSQL_THD thd) +{ + return(&thd->query); +} + +extern "C" int thd_slave_thread(const MYSQL_THD thd) +{ + return(thd->slave_thread); +} + +extern "C" int thd_non_transactional_update(const MYSQL_THD thd) +{ + return(thd->transaction.all.modified_non_trans_table); +} + +extern "C" int thd_binlog_format(const MYSQL_THD thd) +{ + return (int) thd->variables.binlog_format; +} + +extern "C" void thd_mark_transaction_to_rollback(MYSQL_THD thd, bool all) +{ + mark_transaction_to_rollback(thd, all); +} +#endif // INNODB_COMPATIBILITY_HOOKS */ /**************************************************************************** Handling of statement states in functions and triggers. @@ -2538,6 +2696,13 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) limit_found_rows= backup->limit_found_rows; sent_row_count= backup->sent_row_count; client_capabilities= backup->client_capabilities; + /* + If we've left sub-statement mode, reset the fatal error flag. + Otherwise keep the current value, to propagate it up the sub-statement + stack. + */ + if (!in_sub_stmt) + is_fatal_sub_stmt_error= FALSE; if ((options & OPTION_BIN_LOG) && is_update_query(lex->sql_command) && !current_stmt_binlog_row_based) @@ -2552,6 +2717,18 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) } +/** + Mark transaction to rollback and mark error as fatal to a sub-statement. + + @param thd Thread handle + @param all TRUE <=> rollback main transaction. +*/ + +void mark_transaction_to_rollback(THD *thd, bool all) +{ + thd->is_fatal_sub_stmt_error= TRUE; + thd->transaction_rollback_request= all; +} /*************************************************************************** Handling of XA id cacheing ***************************************************************************/ @@ -2559,14 +2736,17 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) pthread_mutex_t LOCK_xid_cache; HASH xid_cache; -static uchar *xid_get_hash_key(const uchar *ptr, size_t *length, +extern "C" uchar *xid_get_hash_key(const uchar *, size_t *, my_bool); +extern "C" void xid_free_hash(void *); + +uchar *xid_get_hash_key(const uchar *ptr, size_t *length, my_bool not_used __attribute__((unused))) { *length=((XID_STATE*)ptr)->xid.key_length(); return ((XID_STATE*)ptr)->xid.key(); } -static void xid_free_hash (void *ptr) +void xid_free_hash(void *ptr) { if (!((XID_STATE*)ptr)->in_thd) my_free((uchar*)ptr, MYF(0)); @@ -3100,13 +3280,13 @@ int THD::binlog_flush_pending_rows_event(bool stmt_end) RETURN VALUE Error code, or 0 if no error. */ -int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query, +int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg, ulong query_len, bool is_trans, bool suppress_use, THD::killed_state killed_status_arg) { DBUG_ENTER("THD::binlog_query"); - DBUG_PRINT("enter", ("qtype=%d, query='%s'", qtype, query)); - DBUG_ASSERT(query && mysql_bin_log.is_open()); + DBUG_PRINT("enter", ("qtype: %d query: '%s'", qtype, query_arg)); + DBUG_ASSERT(query_arg && mysql_bin_log.is_open()); /* If we are not in prelocked mode, mysql_unlock_tables() will be @@ -3162,7 +3342,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query, flush the pending rows event if necessary. */ { - Query_log_event qinfo(this, query, query_len, is_trans, suppress_use, + Query_log_event qinfo(this, query_arg, query_len, is_trans, suppress_use, killed_status_arg); qinfo.flags|= LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F; /* diff --git a/sql/sql_class.h b/sql/sql_class.h index 8d55d06c3ec..d4950ec0f60 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -23,8 +23,7 @@ #include "log.h" #include "rpl_tblmap.h" -struct st_relay_log_info; -typedef st_relay_log_info RELAY_LOG_INFO; +class Relay_log_info; class Query_log_event; class Load_log_event; @@ -39,8 +38,6 @@ enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME }; enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_UPDATE }; enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON, DELAY_KEY_WRITE_ALL }; -enum enum_check_fields -{ CHECK_FIELD_IGNORE, CHECK_FIELD_WARN, CHECK_FIELD_ERROR_FOR_NULL }; enum enum_mark_columns { MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE}; @@ -67,11 +64,23 @@ typedef struct st_user_var_events #define RP_LOCK_LOG_IS_ALREADY_LOCKED 1 #define RP_FORCE_ROTATE 2 +/* + The COPY_INFO structure is used by INSERT/REPLACE code. + The schema of the row counting by the INSERT/INSERT ... ON DUPLICATE KEY + UPDATE code: + If a row is inserted then the copied variable is incremented. + If a row is updated by the INSERT ... ON DUPLICATE KEY UPDATE and the + new data differs from the old one then the copied and the updated + variables are incremented. + The touched variable is incremented if a row was touched by the update part + of the INSERT ... ON DUPLICATE KEY UPDATE no matter whether the row + was actually changed or not. +*/ typedef struct st_copy_info { - ha_rows records; - ha_rows deleted; - ha_rows updated; - ha_rows copied; + ha_rows records; /* Number of processed records */ + ha_rows deleted; /* Number of deleted records */ + ha_rows updated; /* Number of updated records */ + ha_rows copied; /* Number of copied records */ ha_rows error_count; ha_rows touched; /* Number of touched records */ enum enum_duplicates handle_duplicates; @@ -179,7 +188,7 @@ public: Table_ident *table, List<Key_part_spec> &ref_cols, uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg) :Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols), - ref_table(table), ref_columns(cols), + ref_table(table), ref_columns(ref_cols), delete_opt(delete_opt_arg), update_opt(update_opt_arg), match_opt(match_opt_arg) {} @@ -223,38 +232,35 @@ struct system_variables { /* How dynamically allocated system variables are handled: - + The global_system_variables and max_system_variables are "authoritative" They both should have the same 'version' and 'size'. When attempting to access a dynamic variable, if the session version is out of date, then the session version is updated and realloced if neccessary and bytes copied from global to make up for missing data. - */ + */ ulong dynamic_variables_version; char* dynamic_variables_ptr; uint dynamic_variables_head; /* largest valid variable offset */ uint dynamic_variables_size; /* how many bytes are in use */ - + ulonglong myisam_max_extra_sort_file_size; - ulonglong maria_max_sort_file_size; ulonglong myisam_max_sort_file_size; ulonglong max_heap_table_size; ulonglong tmp_table_size; + ulonglong long_query_time; ha_rows select_limit; ha_rows max_join_size; ulong auto_increment_increment, auto_increment_offset; ulong bulk_insert_buff_size; ulong join_buff_size; - ulong long_query_time; ulong max_allowed_packet; ulong max_error_count; ulong max_length_for_sort_data; ulong max_sort_length; ulong max_tmp_tables; ulong max_insert_delayed_threads; - ulong maria_repair_threads; - ulong maria_sort_buff_size; - ulong maria_stats_method; + ulong min_examined_row_limit; ulong multi_range_count; ulong myisam_repair_threads; ulong myisam_sort_buff_size; @@ -302,13 +308,14 @@ struct system_variables my_bool low_priority_updates; my_bool new_mode; - /* + /* compatibility option: - - index usage hints (USE INDEX without a FOR clause) behave as in 5.0 + - index usage hints (USE INDEX without a FOR clause) behave as in 5.0 */ my_bool old_mode; my_bool query_cache_wlock_invalidate; my_bool engine_condition_pushdown; + my_bool keep_files_on_create; my_bool ndb_force_send; my_bool ndb_use_copying_alter_table; my_bool ndb_use_exact_count; @@ -416,6 +423,8 @@ typedef struct system_status_var #define last_system_status_var com_stmt_close +void mark_transaction_to_rollback(THD *thd, bool all); + #ifdef MYSQL_SERVER void free_tmp_table(THD *thd, TABLE *entry); @@ -496,13 +505,6 @@ public: { return strdup_root(mem_root,str); } inline char *strmake(const char *str, size_t size) { return strmake_root(mem_root,str,size); } - inline bool LEX_STRING_make(LEX_STRING *lex_str, const char *str, - size_t size) - { - return ((lex_str->str= - strmake_root(mem_root, str, (lex_str->length= size)))) == 0; - } - inline void *memdup(const void *str, size_t size) { return memdup_root(mem_root,str,size); } inline void *memdup_w_gap(const void *str, size_t size, uint gap) @@ -532,7 +534,7 @@ class Server_side_cursor; - prepared, that is, contain placeholders, - opened as cursors. We maintain 1 to 1 relationship between statement and cursor - if user wants to create another cursor for his - query, we create another statement for it. + query, we create another statement for it. To perform some action with statement we reset THD part to the state of that statement, do the action, and then save back modified state from THD to the statement. It will be changed in near future, and Statement will @@ -583,7 +585,7 @@ public: it. We will see the query_length field as either 0, or the right value for it. Assuming that the write and read of an n-bit memory field in an n-bit - computer is atomic, we can avoid races in the above way. + computer is atomic, we can avoid races in the above way. This printing is needed at least in SHOW PROCESSLIST and SHOW INNODB STATUS. */ @@ -591,6 +593,22 @@ public: uint32 query_length; // current query length Server_side_cursor *cursor; + /** + Name of the current (default) database. + + If there is the current (default) database, "db" contains its name. If + there is no current (default) database, "db" is NULL and "db_length" is + 0. In other words, "db", "db_length" must either be NULL, or contain a + valid database name. + + @note this attribute is set and alloced by the slave SQL thread (for + the THD of that thread); that thread is (and must remain, for now) the + only responsible for freeing this member. + */ + + char *db; + uint db_length; + public: /* This constructor is called for backup statements */ @@ -719,7 +737,7 @@ public: { return (*priv_host ? priv_host : (char *)"%"); } - + bool set_user(char *user_arg); #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -765,13 +783,25 @@ enum prelocked_mode_type {NON_PRELOCKED= 0, PRELOCKED= 1, class Open_tables_state { public: - /* - open_tables - list of regular tables in use by this thread - temporary_tables - list of temp tables in use by this thread - handler_tables - list of tables that were opened with HANDLER OPEN - and are still in use by this thread + /** + List of regular tables in use by this thread. Contains temporary and + base tables that were opened with @see open_tables(). + */ + TABLE *open_tables; + /** + List of temporary tables used by this thread. Contains user-level + temporary tables, created with CREATE TEMPORARY TABLE, and + internal temporary tables, created, e.g., to resolve a SELECT, + or for an intermediate table used in ALTER. + XXX Why are internal temporary tables added to this list? */ - TABLE *open_tables, *temporary_tables, *handler_tables, *derived_tables; + TABLE *temporary_tables; + /** + List of tables that were opened with HANDLER OPEN and are + still in use by this thread. + */ + TABLE *handler_tables; + TABLE *derived_tables; /* During a MySQL session, one can lock tables in two modes: automatic or manual. In automatic mode all necessary tables are locked just before @@ -955,7 +985,7 @@ class THD :public Statement, { public: /* Used to execute base64 coded binlog events in MySQL server */ - RELAY_LOG_INFO* rli_fake; + Relay_log_info* rli_fake; /* Constant for THD::where initialization in the beginning of every query. @@ -1010,23 +1040,24 @@ public: */ char *thread_stack; + /** + Currently selected catalog. + */ + char *catalog; + /* - db - currently selected database - catalog - currently selected catalog - WARNING: some members of THD (currently 'db', 'catalog' and 'query') are - set and alloced by the slave SQL thread (for the THD of that thread); that - thread is (and must remain, for now) the only responsible for freeing these - 3 members. If you add members here, and you add code to set them in - replication, don't forget to free_them_and_set_them_to_0 in replication - properly. For details see the 'err:' label of the handle_slave_sql() - in sql/slave.cc. - */ - char *db, *catalog; + WARNING: some members of THD (currently 'Statement::db', + 'catalog' and 'query') are set and alloced by the slave SQL thread + (for the THD of that thread); that thread is (and must remain, for now) + the only responsible for freeing these 3 members. If you add members + here, and you add code to set them in replication, don't forget to + free_them_and_set_them_to_0 in replication properly. For details see + the 'err:' label of the handle_slave_sql() in sql/slave.cc. + */ + Security_context main_security_ctx; Security_context *security_ctx; - /* remote (peer) port */ - uint16 peer_port; /* Points to info-string that we show in SHOW PROCESSLIST You are supposed to update thd->proc_info only if you have coded @@ -1034,6 +1065,14 @@ public: */ const char *proc_info; + /* + Used in error messages to tell user in what part of MySQL we found an + error. E. g. when where= "having clause", if fix_fields() fails, user + will know that the error was in having clause. + */ + const char *where; + + double tmp_double_value; /* Used in set_var.cc */ ulong client_capabilities; /* What the client supports */ ulong max_client_packet_length; @@ -1041,7 +1080,7 @@ public: /* One thread can hold up to one named user-level lock. This variable points to a lock object if the lock is present. See item_func.cc and - chapter 'Miscellaneous functions', for functions GET_LOCK, RELEASE_LOCK. + chapter 'Miscellaneous functions', for functions GET_LOCK, RELEASE_LOCK. */ User_level_lock *ull; #ifndef DBUG_OFF @@ -1055,14 +1094,12 @@ public: enum enum_server_command command; uint32 server_id; uint32 file_id; // for LOAD DATA INFILE - /* - Used in error messages to tell user in what part of MySQL we found an - error. E. g. when where= "having clause", if fix_fields() fails, user - will know that the error was in having clause. - */ - const char *where; - time_t start_time,time_after_lock,user_time; - time_t connect_time,thr_create_time; // track down slow pthread_create + /* remote (peer) port */ + uint16 peer_port; + time_t start_time, user_time; + ulonglong connect_utime, thr_create_utime; // track down slow pthread_create + ulonglong start_utime, utime_after_lock; + thr_lock_type update_lock_default; Delayed_insert *di; @@ -1299,10 +1336,10 @@ public: mode, row-based binlogging is used for such cases where two auto_increment columns are inserted. */ - inline void record_first_successful_insert_id_in_cur_stmt(ulonglong id) + inline void record_first_successful_insert_id_in_cur_stmt(ulonglong id_arg) { if (first_successful_insert_id_in_cur_stmt == 0) - first_successful_insert_id_in_cur_stmt= id; + first_successful_insert_id_in_cur_stmt= id_arg; } inline ulonglong read_first_successful_insert_id_in_prev_stmt(void) { @@ -1372,7 +1409,6 @@ public: uint tmp_table, global_read_lock; uint server_status,open_options; enum enum_thread_type system_thread; - uint db_length; uint select_number; //number of select (used for EXPLAIN) /* variables.transaction_isolation is reset to this after each commit */ enum_tx_isolation session_tx_isolation; @@ -1399,20 +1435,44 @@ public: bool current_stmt_binlog_row_based; bool locked, some_tables_deleted; bool last_cuted_field; - bool no_errors, password, is_fatal_error; + bool no_errors, password; + /** + Set to TRUE if execution of the current compound statement + can not continue. In particular, disables activation of + CONTINUE or EXIT handlers of stored routines. + Reset in the end of processing of the current user request, in + @see mysql_reset_thd_for_next_command(). + */ + bool is_fatal_error; + /** + Set by a storage engine to request the entire + transaction (that possibly spans multiple engines) to + rollback. Reset in ha_rollback. + */ + bool transaction_rollback_request; + /** + TRUE if we are in a sub-statement and the current error can + not be safely recovered until we left the sub-statement mode. + In particular, disables activation of CONTINUE and EXIT + handlers inside sub-statements. E.g. if it is a deadlock + error and requires a transaction-wide rollback, this flag is + raised (traditionally, MySQL first has to close all the reads + via @see handler::ha_index_or_rnd_end() and only then perform + the rollback). + Reset to FALSE when we leave the sub-statement mode. + */ + bool is_fatal_sub_stmt_error; bool query_start_used, rand_used, time_zone_used; /* for IS NULL => = last_insert_id() fix in remove_eq_conds() */ bool substitute_null_with_insert_id; bool in_lock_tables; bool query_error, bootstrap, cleanup_done; - bool tmp_table_used; + + /** is set if some thread specific value(s) used in a statement. */ + bool thread_specific_used; bool charset_is_system_charset, charset_is_collation_connection; bool charset_is_character_set_filesystem; bool enable_slow_log; /* enable slow log for current statement */ - struct { - bool all:1; - bool stmt:1; - } no_trans_update; bool abort_on_warning; bool got_warning; /* Set on call to push_warning() */ bool no_warnings_for_error; /* no warnings on call to my_error() */ @@ -1439,10 +1499,10 @@ public: ulong ulong_value; ulonglong ulonglong_value; } sys_var_tmp; - + struct { - /* - If true, mysql_bin_log::write(Log_event) call will not write events to + /* + If true, mysql_bin_log::write(Log_event) call will not write events to binlog, and maintain 2 below variables instead (use mysql_bin_log.start_union_events to turn this on) */ @@ -1453,13 +1513,13 @@ public: */ bool unioned_events; /* - If TRUE, at least one mysql_bin_log::write(Log_event e), where - e.cache_stmt == TRUE call has been made after last + If TRUE, at least one mysql_bin_log::write(Log_event e), where + e.cache_stmt == TRUE call has been made after last mysql_bin_log.start_union_events() call. */ bool unioned_events_trans; - - /* + + /* 'queries' (actually SP statements) that run under inside this binlog union have thd->query_id >= first_query_id. */ @@ -1490,7 +1550,7 @@ public: killing mysqld) where it's vital to not allocate excessive and not used memory. Note, that we still don't return error from init_for_queries(): if preallocation fails, we should notice that at the first call to - alloc_root. + alloc_root. */ void init_for_queries(); void change_user(void); @@ -1520,12 +1580,12 @@ public: The query can be logged row-based or statement-based */ ROW_QUERY_TYPE, - + /* The query has to be logged statement-based */ STMT_QUERY_TYPE, - + /* The query represents a change to a table in the "mysql" database and is currently mapped to ROW_QUERY_TYPE. @@ -1533,7 +1593,7 @@ public: MYSQL_QUERY_TYPE, QUERY_TYPE_COUNT }; - + int binlog_query(enum_binlog_query_type qtype, char const *query, ulong query_len, bool is_trans, bool suppress_use, @@ -1571,10 +1631,24 @@ public: pthread_mutex_unlock(&mysys_var->mutex); } inline time_t query_start() { query_start_used=1; return start_time; } - inline void set_time() { if (user_time) start_time=time_after_lock=user_time; else time_after_lock=time(&start_time); } - inline void end_time() { time(&start_time); } - inline void set_time(time_t t) { time_after_lock=start_time=user_time=t; } - inline void lock_time() { time(&time_after_lock); } + inline void set_time() + { + if (user_time) + { + start_time= user_time; + start_utime= utime_after_lock= my_micro_time(); + } + else + start_utime= utime_after_lock= my_micro_time_and_time(&start_time); + } + inline void set_current_time() { start_time= my_time(MY_WME); } + inline void set_time(time_t t) + { + start_time= user_time= t; + start_utime= utime_after_lock= my_micro_time(); + } + void set_time_after_lock() { utime_after_lock= my_micro_time(); } + ulonglong current_utime() { return my_micro_time(); } inline ulonglong found_rows(void) { return limit_found_rows; @@ -1600,6 +1674,10 @@ public: return alloc_root(&transaction.mem_root,size); } + LEX_STRING *make_lex_string(LEX_STRING *lex_str, + const char* str, uint length, + bool allocate_lex_string); + bool convert_string(LEX_STRING *to, CHARSET_INFO *to_cs, const char *from, uint from_length, CHARSET_INFO *from_cs); @@ -1679,7 +1757,7 @@ public: inline bool really_abort_on_warning() { return (abort_on_warning && - (!no_trans_update.stmt || + (!transaction.stmt.modified_non_trans_table || (variables.sql_mode & MODE_STRICT_ALL_TABLES))); } void set_status_var_init(); @@ -1737,16 +1815,31 @@ public: if ((temporary_tables == NULL) && (in_sub_stmt == 0) && (system_thread != SYSTEM_THREAD_NDBCLUSTER_BINLOG)) { - current_stmt_binlog_row_based= + current_stmt_binlog_row_based= test(variables.binlog_format == BINLOG_FORMAT_ROW); } } - /* - Initialize the current database from a NULL-terminated string with length - If we run out of memory, we free the current database and return TRUE. - This way the user will notice the error as there will be no current - database selected (in addition to the error message set by malloc). + /** + Set the current database; use deep copy of C-string. + + @param new_db a pointer to the new database name. + @param new_db_len length of the new database name. + + Initialize the current database from a NULL-terminated string with + length. If we run out of memory, we free the current database and + return TRUE. This way the user will notice the error as there will be + no current database selected (in addition to the error message set by + malloc). + + @note This operation just sets {db, db_length}. Switching the current + database usually involves other actions, like switching other database + attributes including security context. In the future, this operation + will be made private and more convenient interface will be provided. + + @return Operation status + @retval FALSE Success + @retval TRUE Out-of-memory error */ bool set_db(const char *new_db, size_t new_db_len) { @@ -1761,6 +1854,18 @@ public: db_length= db ? new_db_len : 0; return new_db && !db; } + + /** + Set the current database; use shallow copy of C-string. + + @param new_db a pointer to the new database name. + @param new_db_len length of the new database name. + + @note This operation just sets {db, db_length}. Switching the current + database usually involves other actions, like switching other database + attributes including security context. In the future, this operation + will be made private and more convenient interface will be provided. + */ void reset_db(char *new_db, size_t new_db_len) { db= new_db; @@ -1953,9 +2058,30 @@ public: }; +#define ESCAPE_CHARS "ntrb0ZN" // keep synchronous with READ_INFO::unescape + + +/* + List of all possible characters of a numeric value text representation. +*/ +#define NUMERIC_CHARS ".0123456789e+-" + + class select_export :public select_to_file { uint field_term_length; int field_sep_char,escape_char,line_sep_char; + /* + The is_ambiguous_field_sep field is true if a value of the field_sep_char + field is one of the 'n', 't', 'r' etc characters + (see the READ_INFO::unescape method and the ESCAPE_CHARS constant value). + */ + bool is_ambiguous_field_sep; + /* + The is_unsafe_field_sep field is true if a value of the field_sep_char + field is one of the '0'..'9', '+', '-', '.' and 'e' characters + (see the NUMERIC_CHARS constant value). + */ + bool is_unsafe_field_sep; bool fixed_row_size; public: select_export(sql_exchange *ex) :select_to_file(ex) {} @@ -2003,8 +2129,8 @@ class select_insert :public select_result_interceptor { class select_create: public select_insert { ORDER *group; TABLE_LIST *create_table; - TABLE_LIST *select_tables; HA_CREATE_INFO *create_info; + TABLE_LIST *select_tables; Alter_info *alter_info; Field **field; public: @@ -2035,8 +2161,8 @@ public: #include <myisam.h> -/* - Param to create temporary tables when doing SELECT:s +/* + Param to create temporary tables when doing SELECT:s NOTE This structure is copied using memcpy as a part of JOIN. */ @@ -2064,8 +2190,8 @@ public: uint quick_group; bool using_indirect_summary_function; /* If >0 convert all blob fields to varchar(convert_blob_length) */ - uint convert_blob_length; - CHARSET_INFO *table_charset; + uint convert_blob_length; + CHARSET_INFO *table_charset; bool schema_table; /* True if GROUP BY and its aggregate functions are already computed @@ -2199,7 +2325,7 @@ public: else db= db_arg; } - inline Table_ident(LEX_STRING table_arg) + inline Table_ident(LEX_STRING table_arg) :table(table_arg), sel((SELECT_LEX_UNIT *)0) { db.str=0; @@ -2245,7 +2371,7 @@ class user_var_entry }; /* - Unique -- class for unique (removing of duplicates). + Unique -- class for unique (removing of duplicates). Puts all values to the TREE. If the tree becomes too big, it's dumped to the file. User can request sorted values, or just iterate through them. In the last case tree merging is performed in @@ -2279,9 +2405,9 @@ public: } bool get(TABLE *table); - static double get_use_cost(uint *buffer, uint nkeys, uint key_size, + static double get_use_cost(uint *buffer, uint nkeys, uint key_size, ulonglong max_in_memory_size); - inline static int get_cost_calc_buff_size(ulong nkeys, uint key_size, + inline static int get_cost_calc_buff_size(ulong nkeys, uint key_size, ulonglong max_in_memory_size) { register ulonglong max_elems_in_tree= @@ -2336,7 +2462,7 @@ class multi_update :public select_result_interceptor uint table_count; /* List of tables referenced in the CHECK OPTION condition of - the updated view excluding the updated table. + the updated view excluding the updated table. */ List <TABLE> unupdated_check_opt_tables; Copy_field *copy_field; @@ -2397,10 +2523,14 @@ public: #define CF_HAS_ROW_COUNT 2 #define CF_STATUS_COMMAND 4 #define CF_SHOW_TABLE_COMMAND 8 +#define CF_WRITE_LOGS_COMMAND 16 /* Functions in sql_class.cc */ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var); + void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var, STATUS_VAR *dec_var); +void mark_transaction_to_rollback(THD *thd, bool all); + #endif /* MYSQL_SERVER */ diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 26b7098e27c..6bb0f62d843 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -97,7 +97,7 @@ static int get_or_create_user_conn(THD *thd, const char *user, 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; + uc->reset_utime= thd->thr_create_utime; if (my_hash_insert(&hash_user_connections, (uchar*) uc)) { my_free((char*) uc,0); @@ -223,16 +223,16 @@ void decrease_user_connections(USER_CONN *uc) void time_out_user_resource_limits(THD *thd, USER_CONN *uc) { - time_t check_time = thd->start_time ? thd->start_time : time(NULL); + ulonglong check_time= thd->start_utime; DBUG_ENTER("time_out_user_resource_limits"); /* If more than a hour since last check, reset resource checking */ - if (check_time - uc->intime >= 3600) + if (check_time - uc->reset_utime >= LL(3600000000)) { uc->questions=1; uc->updates=0; uc->conn_per_hour=0; - uc->intime=check_time; + uc->reset_utime= check_time; } DBUG_VOID_RETURN; @@ -1008,6 +1008,11 @@ void prepare_new_connection_state(THD* thd) if (thd->client_capabilities & CLIENT_COMPRESS) thd->net.compress=1; // Use compression + /* + Much of this is duplicated in create_embedded_thd() for the + embedded server library. + TODO: refactor this to avoid code duplication there + */ thd->version= refresh_version; thd->proc_info= 0; thd->command= COM_SLEEP; @@ -1053,8 +1058,8 @@ void prepare_new_connection_state(THD* thd) 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); + ulong launch_time= (ulong) ((thd->thr_create_utime= my_micro_time()) - + thd->connect_utime); if (thread_scheduler.init_new_connection_thread()) { @@ -1063,7 +1068,7 @@ pthread_handler_t handle_one_connection(void *arg) thread_scheduler.end_thread(thd,0); return 0; } - if (launch_time >= slow_launch_time) + if (launch_time >= slow_launch_time*1000000L) statistic_increment(slow_launch_threads,&LOCK_status); /* diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 43d84740f0b..cd7ad048802 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -39,6 +39,10 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, static long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path); static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error); +static void mysql_change_db_impl(THD *thd, + LEX_STRING *new_db_name, + ulong new_db_access, + CHARSET_INFO *new_db_charset); /* Database lock hash */ @@ -59,8 +63,10 @@ typedef struct my_dblock_st lock_db key. */ -static uchar* lock_db_get_key(my_dblock_t *ptr, size_t *length, - my_bool not_used __attribute__((unused))) +extern "C" uchar* lock_db_get_key(my_dblock_t *, size_t *, my_bool not_used); + +uchar* lock_db_get_key(my_dblock_t *ptr, size_t *length, + my_bool not_used __attribute__((unused))) { *length= ptr->name_length; return (uchar*) ptr->name; @@ -71,7 +77,9 @@ static uchar* lock_db_get_key(my_dblock_t *ptr, size_t *length, Free lock_db hash element. */ -static void lock_db_free_element(void *ptr) +extern "C" void lock_db_free_element(void *ptr); + +void lock_db_free_element(void *ptr) { my_free(ptr, MYF(0)); } @@ -155,8 +163,11 @@ typedef struct my_dbopt_st Function we use in the creation of our hash to get key. */ -static uchar* dboptions_get_key(my_dbopt_t *opt, size_t *length, - my_bool not_used __attribute__((unused))) +extern "C" uchar* dboptions_get_key(my_dbopt_t *opt, size_t *length, + my_bool not_used); + +uchar* dboptions_get_key(my_dbopt_t *opt, size_t *length, + my_bool not_used __attribute__((unused))) { *length= opt->name_length; return (uchar*) opt->name; @@ -182,7 +193,9 @@ static inline void write_to_binlog(THD *thd, char *query, uint q_len, Function to free dboptions hash element */ -static void free_dbopt(void *dbopt) +extern "C" void free_dbopt(void *dbopt); + +void free_dbopt(void *dbopt) { my_free((uchar*) dbopt, MYF(0)); } @@ -988,7 +1001,7 @@ exit: it to 0. */ if (thd->db && !strcmp(thd->db, db)) - thd->set_db(NULL, 0); + mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); VOID(pthread_mutex_unlock(&LOCK_mysql_create_db)); start_waiting_global_read_lock(thd); exit2: @@ -1113,7 +1126,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, } } if (thd->killed || - (tot_list && mysql_rm_table_part2_with_lock(thd, tot_list, 1, 0, 1))) + (tot_list && mysql_rm_table_part2(thd, tot_list, 1, 0, 1, 1))) goto err; /* Remove RAID directories */ @@ -1346,22 +1359,108 @@ static void mysql_change_db_impl(THD *thd, } + +/** + Backup the current database name before switch. + + @param[in] thd thread handle + @param[in, out] saved_db_name IN: "str" points to a buffer where to store + the old database name, "length" contains the + buffer size + OUT: if the current (default) database is + not NULL, its name is copied to the + buffer pointed at by "str" + and "length" is updated accordingly. + Otherwise "str" is set to NULL and + "length" is set to 0. +*/ + +static void backup_current_db_name(THD *thd, + LEX_STRING *saved_db_name) +{ + if (!thd->db) + { + /* No current (default) database selected. */ + + saved_db_name->str= NULL; + saved_db_name->length= 0; + } + else + { + strmake(saved_db_name->str, thd->db, saved_db_name->length); + saved_db_name->length= thd->db_length; + } +} + + +/** + Return TRUE if db1_name is equal to db2_name, FALSE otherwise. + + The function allows to compare database names according to the MySQL + rules. The database names db1 and db2 are equal if: + - db1 is NULL and db2 is NULL; + or + - db1 is not-NULL, db2 is not-NULL, db1 is equal (ignoring case) to + db2 in system character set (UTF8). +*/ + +static inline bool +cmp_db_names(const char *db1_name, + const char *db2_name) +{ + return + /* db1 is NULL and db2 is NULL */ + !db1_name && !db2_name || + + /* db1 is not-NULL, db2 is not-NULL, db1 == db2. */ + db1_name && db2_name && + my_strcasecmp(system_charset_info, db1_name, db2_name) == 0; +} + + /** - @brief Change the current database. + @brief Change the current database and its attributes unconditionally. @param thd thread handle - @param name database name - @param force_switch if this flag is set (TRUE), mysql_change_db() will - switch to NULL db if the specified database is not - available anymore. Corresponding warning will be - thrown in this case. This flag is used to change - database in stored-routine-execution code. - - @details Check that the database name corresponds to a valid and existent - database, check access rights (unless called with no_access_check), and - set the current database. This function is called to change the current - database upon user request (COM_CHANGE_DB command) or temporarily, to - execute a stored routine. + @param new_db_name database name + @param force_switch if force_switch is FALSE, then the operation will fail if + + - new_db_name is NULL or empty; + + - OR new database name is invalid + (check_db_name() failed); + + - OR user has no privilege on the new database; + + - OR new database does not exist; + + if force_switch is TRUE, then + + - if new_db_name is NULL or empty, the current + database will be NULL, @@collation_database will + be set to @@collation_server, the operation will + succeed. + + - if new database name is invalid + (check_db_name() failed), the current database + will be NULL, @@collation_database will be set to + @@collation_server, but the operation will fail; + + - user privileges will not be checked + (THD::db_access however is updated); + + TODO: is this really the intention? + (see sp-security.test). + + - if new database does not exist,the current database + will be NULL, @@collation_database will be set to + @@collation_server, a warning will be thrown, the + operation will succeed. + + @details The function checks that the database name corresponds to a + valid and existent database, checks access rights and changes the current + database with database attributes (@@collation_database session variable, + THD::db_access). This function is not the only way to switch the database that is currently employed. When the replication slave thread switches the @@ -1397,10 +1496,15 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) { if (force_switch) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR)); + /* + This can happen only if we're switching the current database back + after loading stored program. The thing is that loading of stored + program can happen when there is no current database. - /* Change db to NULL. */ + TODO: actually, new_db_name and new_db_name->str seem to be always + non-NULL. In case of stored program, new_db_name->str == "" and + new_db_name->length == 0. + */ mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); @@ -1417,7 +1521,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) if (my_strcasecmp(system_charset_info, new_db_name->str, INFORMATION_SCHEMA_NAME.str) == 0) { - /* Switch database to INFORMATION_SCHEMA. */ + /* Switch the current database to INFORMATION_SCHEMA. */ mysql_change_db_impl(thd, &INFORMATION_SCHEMA_NAME, SELECT_ACL, system_charset_info); @@ -1444,8 +1548,8 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) even if we are called from sp_head::execute(). It's next to impossible however to get this error when we are called - from sp_head::execute(). But let's switch database to NULL in this case - to be sure. + from sp_head::execute(). But let's switch the current database to NULL + in this case to be sure. */ if (check_db_name(&new_db_file_name)) @@ -1454,10 +1558,8 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) my_free(new_db_file_name.str, MYF(0)); if (force_switch) - { - /* Change db to NULL. */ mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); - } + DBUG_RETURN(TRUE); } @@ -1492,6 +1594,8 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) { if (force_switch) { + /* Throw a warning and free new_db_file_name. */ + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_BAD_DB_ERROR, ER(ER_BAD_DB_ERROR), new_db_file_name.str); @@ -1502,12 +1606,19 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); + /* The operation succeed. */ + DBUG_RETURN(FALSE); } else { + /* Report an error and free new_db_file_name. */ + my_error(ER_BAD_DB_ERROR, MYF(0), new_db_file_name.str); my_free(new_db_file_name.str, MYF(0)); + + /* The operation failed. */ + DBUG_RETURN(TRUE); } } @@ -1525,6 +1636,43 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) } +/** + Change the current database and its attributes if needed. + + @param thd thread handle + @param new_db_name database name + @param[in, out] saved_db_name IN: "str" points to a buffer where to store + the old database name, "length" contains the + buffer size + OUT: if the current (default) database is + not NULL, its name is copied to the + buffer pointed at by "str" + and "length" is updated accordingly. + Otherwise "str" is set to NULL and + "length" is set to 0. + @param force_switch @see mysql_change_db() + @param[out] cur_db_changed out-flag to indicate whether the current + database has been changed (valid only if + the function suceeded) +*/ + +bool mysql_opt_change_db(THD *thd, + const LEX_STRING *new_db_name, + LEX_STRING *saved_db_name, + bool force_switch, + bool *cur_db_changed) +{ + *cur_db_changed= !cmp_db_names(thd->db, new_db_name->str); + + if (!*cur_db_changed) + return FALSE; + + backup_current_db_name(thd, saved_db_name); + + return mysql_change_db(thd, new_db_name, force_switch); +} + + static int lock_databases(THD *thd, const char *db1, uint length1, const char *db2, uint length2) @@ -1820,7 +1968,7 @@ bool mysql_rename_db(THD *thd, LEX_STRING *old_db, LEX_STRING *new_db) /* Step9: Let's do "use newdb" if we renamed the current database */ if (change_to_newdb) - error|= mysql_change_db(thd, new_db, 0); + error|= mysql_change_db(thd, new_db, FALSE); exit: pthread_mutex_lock(&LOCK_lock_db); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 43e5bbf968f..f53d01aa30b 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -341,6 +341,9 @@ cleanup: delete select; transactional_table= table->file->has_transactions(); + if (!transactional_table && deleted > 0) + thd->transaction.stmt.modified_non_trans_table= TRUE; + /* See similar binlogging code in sql_update.cc, for comments */ if ((error < 0) || (deleted && !transactional_table)) { @@ -364,9 +367,10 @@ cleanup: error=1; } } - if (!transactional_table) - thd->no_trans_update.all= TRUE; + if (thd->transaction.stmt.modified_non_trans_table) + thd->transaction.all.modified_non_trans_table= TRUE; } + DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table); free_underlaid_joins(thd, select_lex); if (transactional_table) { @@ -677,20 +681,22 @@ bool multi_delete::send_data(List<Item> &values) if (table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_DELETE, TRG_ACTION_BEFORE, FALSE)) - DBUG_RETURN(1); + DBUG_RETURN(1); table->status|= STATUS_DELETED; if (!(error=table->file->ha_delete_row(table->record[0]))) { - deleted++; + deleted++; + if (!table->file->has_transactions()) + thd->transaction.stmt.modified_non_trans_table= TRUE; if (table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_DELETE, TRG_ACTION_AFTER, FALSE)) - DBUG_RETURN(1); + DBUG_RETURN(1); } else { - table->file->print_error(error,MYF(0)); - DBUG_RETURN(1); + table->file->print_error(error,MYF(0)); + DBUG_RETURN(1); } } else @@ -740,6 +746,7 @@ void multi_delete::send_error(uint errcode,const char *err) error= 1; send_eof(); } + DBUG_ASSERT(!normal_tables || !deleted || thd->transaction.stmt.modified_non_trans_table); DBUG_VOID_RETURN; } @@ -753,7 +760,7 @@ void multi_delete::send_error(uint errcode,const char *err) int multi_delete::do_deletes() { - int local_error= 0, counter= 0, error; + int local_error= 0, counter= 0, tmp_error; bool will_batch; DBUG_ENTER("do_deletes"); DBUG_ASSERT(do_delete); @@ -768,6 +775,7 @@ int multi_delete::do_deletes() for (; table_being_deleted; table_being_deleted= table_being_deleted->next_local, counter++) { + ha_rows last_deleted= deleted; TABLE *table = table_being_deleted->table; if (tempfiles[counter]->get(table)) { @@ -806,14 +814,16 @@ int multi_delete::do_deletes() break; } } - if (will_batch && (error= table->file->end_bulk_delete())) + if (will_batch && (tmp_error= table->file->end_bulk_delete())) { if (!local_error) { - local_error= error; + local_error= tmp_error; table->file->print_error(local_error,MYF(0)); } } + if (last_deleted != deleted && !table->file->has_transactions()) + thd->transaction.stmt.modified_non_trans_table= TRUE; end_read_record(&info); if (thd->killed && !local_error) local_error= 1; @@ -852,7 +862,6 @@ bool multi_delete::send_eof() { query_cache_invalidate3(thd, delete_tables, 1); } - if ((local_error == 0) || (deleted && normal_tables)) { if (mysql_bin_log.is_open()) @@ -867,9 +876,11 @@ bool multi_delete::send_eof() local_error=1; // Log write failed: roll back the SQL statement } } - if (!transactional_tables) - thd->no_trans_update.all= TRUE; + if (thd->transaction.stmt.modified_non_trans_table) + thd->transaction.all.modified_non_trans_table= TRUE; } + DBUG_ASSERT(!normal_tables || !deleted || thd->transaction.stmt.modified_non_trans_table); + /* Commit or rollback the current SQL statement */ if (transactional_tables) if (ha_autocommit_or_rollback(thd,local_error > 0)) @@ -906,9 +917,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) char path[FN_REFLEN]; TABLE *table; 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)); @@ -971,18 +980,6 @@ 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 (log_type) - { - 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 // crashes, replacement works. *(path + path_length - reg_ext_length)= // '\0'; @@ -1008,14 +1005,6 @@ end: VOID(pthread_mutex_lock(&LOCK_open)); unlock_table_name(thd, table_list); VOID(pthread_mutex_unlock(&LOCK_open)); - - if (opt_slow_log && (closed_log_tables & QUERY_LOG_SLOW)) - logger.reopen_log_table(QUERY_LOG_SLOW); - - if (opt_log && (closed_log_tables & QUERY_LOG_GENERAL)) - logger.reopen_log_table(QUERY_LOG_GENERAL); - if (lock_logger) - logger.unlock(); } else if (error) { diff --git a/sql/sql_error.cc b/sql/sql_error.cc index 5ebeba6109a..8bdb2e59ed5 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -137,6 +137,9 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, level= MYSQL_ERROR::WARN_LEVEL_ERROR; } + if (thd->handle_error(code, level)) + DBUG_RETURN(NULL); + if (thd->spcont && thd->spcont->handle_error(code, level, thd)) { diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 300ec7f3c62..1adce48c539 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -119,6 +119,44 @@ static void mysql_ha_hash_free(TABLE_LIST *tables) my_free((char*) tables, MYF(0)); } +/** + Close a HANDLER table. + + @param thd Thread identifier. + @param tables A list of tables with the first entry to close. + + @note Though this function takes a list of tables, only the first list entry + will be closed. + @note Broadcasts refresh if it closed the table. +*/ + +static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) +{ + TABLE **table_ptr; + + /* + Though we could take the table pointer from hash_tables->table, + we must follow the thd->handler_tables chain anyway, as we need the + address of the 'next' pointer referencing this table + for close_thread_table(). + */ + for (table_ptr= &(thd->handler_tables); + *table_ptr && (*table_ptr != tables->table); + table_ptr= &(*table_ptr)->next) + ; + + if (*table_ptr) + { + (*table_ptr)->file->ha_index_or_rnd_end(); + VOID(pthread_mutex_lock(&LOCK_open)); + if (close_thread_table(thd, table_ptr)) + { + /* Tell threads waiting for refresh that something has happened */ + broadcast_refresh(); + } + VOID(pthread_mutex_unlock(&LOCK_open)); + } +} /* Open a HANDLER table. @@ -145,7 +183,7 @@ static void mysql_ha_hash_free(TABLE_LIST *tables) bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) { - TABLE_LIST *hash_tables; + TABLE_LIST *hash_tables = NULL; char *db, *name, *alias; uint dblen, namelen, aliaslen, counter; int error; @@ -197,7 +235,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) { if (! reopen) my_error(ER_ILLEGAL_HA, MYF(0), tables->alias); - mysql_ha_close(thd, tables); goto err; } @@ -225,11 +262,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) /* add to hash */ if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables)) - { - my_free((char*) hash_tables, MYF(0)); - mysql_ha_close(thd, tables); goto err; - } } if (! reopen) @@ -238,13 +271,17 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) DBUG_RETURN(FALSE); err: + if (hash_tables) + my_free((char*) hash_tables, MYF(0)); + if (tables->table) + mysql_ha_close_table(thd, tables); DBUG_PRINT("exit",("ERROR")); DBUG_RETURN(TRUE); } /* - Close a HANDLER table. + Close a HANDLER table by alias or table name SYNOPSIS mysql_ha_close() @@ -252,9 +289,8 @@ err: tables A list of tables with the first entry to close. DESCRIPTION - Though this function takes a list of tables, only the first list entry - will be closed. - Broadcasts refresh if it closed the table. + Closes the table that is associated (on the handler tables hash) with the + name (table->alias) of the specified table. RETURN FALSE ok @@ -264,7 +300,6 @@ err: bool mysql_ha_close(THD *thd, TABLE_LIST *tables) { TABLE_LIST *hash_tables; - TABLE **table_ptr; DBUG_ENTER("mysql_ha_close"); DBUG_PRINT("enter",("'%s'.'%s' as '%s'", tables->db, tables->table_name, tables->alias)); @@ -273,28 +308,7 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables) (uchar*) tables->alias, strlen(tables->alias) + 1))) { - /* - Though we could take the table pointer from hash_tables->table, - we must follow the thd->handler_tables chain anyway, as we need the - address of the 'next' pointer referencing this table - for close_thread_table(). - */ - for (table_ptr= &(thd->handler_tables); - *table_ptr && (*table_ptr != hash_tables->table); - table_ptr= &(*table_ptr)->next) - ; - - if (*table_ptr) - { - (*table_ptr)->file->ha_index_or_rnd_end(); - VOID(pthread_mutex_lock(&LOCK_open)); - if (close_thread_table(thd, table_ptr)) - { - /* Tell threads waiting for refresh that something has happened */ - broadcast_refresh(); - } - VOID(pthread_mutex_unlock(&LOCK_open)); - } + mysql_ha_close_table(thd, hash_tables); hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables); } else @@ -430,7 +444,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, cond->cleanup(); // File was reopened if ((!cond->fixed && cond->fix_fields(thd, &cond)) || cond->check_cols(1)) - goto err0; + goto err; } if (keyname) @@ -438,13 +452,13 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, if ((keyno=find_type(keyname, &table->s->keynames, 1+2)-1)<0) { my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), keyname, tables->alias); - goto err0; + goto err; } } if (insert_fields(thd, &thd->lex->select_lex.context, tables->db, tables->alias, &it, 0)) - goto err0; + goto err; protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); @@ -541,8 +555,8 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, table->file->ha_index_or_rnd_end(); table->file->ha_index_init(keyno, 1); key_copy(key, table->record[0], table->key_info + keyno, key_len); - error= table->file->index_read(table->record[0], - key, keypart_map, ha_rkey_mode); + error= table->file->index_read_map(table->record[0], + key, keypart_map, ha_rkey_mode); mode=rkey_to_rnext[(int)ha_rkey_mode]; break; } @@ -758,48 +772,3 @@ 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) - { - /* - Some elements in open table list, for example placeholders used for - name-locking, can have alias set to 0. - */ - if (table->alias) - { - TABLE_LIST *hash_tables; - if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash, - (uchar*) 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 023bd1fec94..c1962c8c650 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -294,9 +294,9 @@ int get_topics_for_keyword(THD *thd, TABLE *topics, TABLE *relations, rkey_id->store((longlong) key_id, TRUE); rkey_id->get_key_image(buff, rkey_id->pack_length(), Field::itRAW); - int key_res= relations->file->index_read(relations->record[0], - buff, (key_part_map) 1, - HA_READ_KEY_EXACT); + int key_res= relations->file->index_read_map(relations->record[0], + buff, (key_part_map) 1, + HA_READ_KEY_EXACT); for ( ; !key_res && key_id == (int16) rkey_id->val_int() ; @@ -308,8 +308,8 @@ int get_topics_for_keyword(THD *thd, TABLE *topics, TABLE *relations, field->store((longlong) topic_id, TRUE); field->get_key_image(topic_id_buff, field->pack_length(), Field::itRAW); - if (!topics->file->index_read(topics->record[0], topic_id_buff, - (key_part_map)1, HA_READ_KEY_EXACT)) + if (!topics->file->index_read_map(topics->record[0], topic_id_buff, + (key_part_map)1, HA_READ_KEY_EXACT)) { memorize_variant_topic(thd,topics,count,find_fields, names,name,description,example); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index d7267e6ecf6..0774b5ae42e 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -397,7 +397,7 @@ void upgrade_lock_type(THD *thd, thr_lock_type *lock_type, if (duplic == DUP_UPDATE || duplic == DUP_REPLACE && *lock_type == TL_WRITE_CONCURRENT_INSERT) { - *lock_type= TL_WRITE; + *lock_type= TL_WRITE_DEFAULT; return; } @@ -426,7 +426,6 @@ void upgrade_lock_type(THD *thd, thr_lock_type *lock_type, client connection and the delayed thread. */ if (specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE) || - thd->slave_thread || thd->variables.max_insert_delayed_threads == 0 || thd->prelocked_mode || thd->lex->uses_stored_routines()) @@ -434,6 +433,14 @@ void upgrade_lock_type(THD *thd, thr_lock_type *lock_type, *lock_type= TL_WRITE; return; } + if (thd->slave_thread) + { + /* Try concurrent insert */ + *lock_type= (duplic == DUP_UPDATE || duplic == DUP_REPLACE) ? + TL_WRITE : TL_WRITE_CONCURRENT_INSERT; + return; + } + bool log_on= (thd->options & OPTION_BIN_LOG || ! (thd->security_ctx->master_access & SUPER_ACL)); if (global_system_variables.binlog_format == BINLOG_FORMAT_STMT && @@ -549,6 +556,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, int error, res; bool transactional_table, joins_freed= FALSE; bool changed; + bool was_insert_delayed= (table_list->lock_type == TL_WRITE_DELAYED); uint value_count; ulong counter = 1; ulonglong id; @@ -661,7 +669,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, /* Fill in the given fields and dump it to the table file */ - info.records= info.deleted= info.copied= info.updated= 0; + bzero((char*) &info,sizeof(info)); info.ignore= ignore; info.handle_duplicates=duplic; info.update_fields= &update_fields; @@ -712,7 +720,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, if (lock_type != TL_WRITE_DELAYED && !thd->prelocked_mode) table->file->ha_start_bulk_insert(values_list.elements); - thd->no_trans_update.stmt= FALSE; thd->abort_on_warning= (!ignore && (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES))); @@ -832,14 +839,16 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, } transactional_table= table->file->has_transactions(); - if ((changed= (info.copied || info.deleted || info.updated))) + if ((changed= (info.copied || info.deleted || info.updated)) || + was_insert_delayed) { /* Invalidate the table in the query cache if something changed. For the transactional algorithm to work the invalidation must be before binlog writing and ha_autocommit_or_rollback */ - query_cache_invalidate3(thd, table_list, 1); + if (changed) + query_cache_invalidate3(thd, table_list, 1); if (error <= 0 || !transactional_table) { if (mysql_bin_log.is_open()) @@ -880,10 +889,12 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, error=1; } } - if (!transactional_table) - thd->no_trans_update.all= TRUE; + if (thd->transaction.stmt.modified_non_trans_table) + thd->transaction.all.modified_non_trans_table= TRUE; } } + DBUG_ASSERT(transactional_table || !changed || + thd->transaction.stmt.modified_non_trans_table); if (transactional_table) error=ha_autocommit_or_rollback(thd,error); @@ -1293,7 +1304,7 @@ static int last_uniq_key(TABLE *table,uint keynr) then both on update triggers will work instead. Similarly both on delete triggers will be invoked if we will delete conflicting records. - Sets thd->no_trans_update.stmt to TRUE if table which is updated didn't have + Sets thd->transaction.stmt.modified_non_trans_table to TRUE if table which is updated didn't have transactions. RETURN VALUE @@ -1387,9 +1398,9 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) } } key_copy((uchar*) key,table->record[0],table->key_info+key_nr,0); - if ((error=(table->file->index_read_idx(table->record[1],key_nr, - (uchar*) key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)))) + if ((error=(table->file->index_read_idx_map(table->record[1],key_nr, + (uchar*) key, HA_WHOLE_KEY, + HA_READ_KEY_EXACT)))) goto err; } if (info->handle_duplicates == DUP_UPDATE) @@ -1421,7 +1432,12 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) goto before_trg_err; table->file->restore_auto_increment(prev_insert_id); - if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) || + if (table->next_number_field) + table->file->adjust_next_insert_id_after_explicit_value( + table->next_number_field->val_int()); + info->touched++; + if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ && + !bitmap_is_subset(table->write_set, table->read_set)) || compare_record(table)) { if ((error=table->file->ha_update_row(table->record[1], @@ -1448,9 +1464,6 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) 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)); @@ -1511,7 +1524,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) goto err; info->deleted++; if (!table->file->has_transactions()) - thd->no_trans_update.stmt= TRUE; + thd->transaction.stmt.modified_non_trans_table= TRUE; if (table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_DELETE, TRG_ACTION_AFTER, TRUE)) @@ -1552,7 +1565,7 @@ ok_or_after_trg_err: if (key) my_safe_afree(key,table->s->max_unique_length,MAX_KEY_LENGTH); if (!table->file->has_transactions()) - thd->no_trans_update.stmt= TRUE; + thd->transaction.stmt.modified_non_trans_table= TRUE; DBUG_RETURN(trg_error); err: @@ -1761,18 +1774,18 @@ Delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list) thd->proc_info="waiting for delay_list"; pthread_mutex_lock(&LOCK_delayed_insert); // Protect master list I_List_iterator<Delayed_insert> it(delayed_threads); - Delayed_insert *tmp; - while ((tmp=it++)) + Delayed_insert *di; + while ((di= it++)) { - if (!strcmp(tmp->thd.db, table_list->db) && - !strcmp(table_list->table_name, tmp->table->s->table_name.str)) + if (!strcmp(table_list->db, di->table_list.db) && + !strcmp(table_list->table_name, di->table_list.table_name)) { - tmp->lock(); + di->lock(); break; } } pthread_mutex_unlock(&LOCK_delayed_insert); // For unlink from list - return tmp; + return di; } @@ -1798,21 +1811,41 @@ Delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list) Two latter cases indicate a request for lock upgrade. XXX: why do we regard INSERT DELAYED into a view as an error and - do not simply a lock upgrade? + do not simply perform a lock upgrade? + + TODO: The approach with using two mutexes to work with the + delayed thread list -- LOCK_delayed_insert and + LOCK_delayed_create -- is redundant, and we only need one of + them to protect the list. The reason we have two locks is that + we do not want to block look-ups in the list while we're waiting + for the newly created thread to open the delayed table. However, + this wait itself is redundant -- we always call get_local_table + later on, and there wait again until the created thread acquires + a table lock. + + As is redundant the concept of locks_in_memory, since we already + have another counter with similar semantics - tables_in_use, + both of them are devoted to counting the number of producers for + a given consumer (delayed insert thread), only at different + stages of producer-consumer relationship. + + 'dead' and 'status' variables in Delayed_insert are redundant + too, since there is already 'di->thd.killed' and + di->stacked_inserts. */ static bool delayed_get_table(THD *thd, TABLE_LIST *table_list) { int error; - Delayed_insert *tmp; + Delayed_insert *di; DBUG_ENTER("delayed_get_table"); /* Must be set in the parser */ DBUG_ASSERT(table_list->db); /* Find the thread which handles this table. */ - if (!(tmp=find_handler(thd,table_list))) + if (!(di= find_handler(thd, table_list))) { /* No match. Create a new thread to handle the table, but @@ -1826,9 +1859,9 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list) The first search above was done without LOCK_delayed_create. Another thread might have created the handler in between. Search again. */ - if (! (tmp= find_handler(thd, table_list))) + if (! (di= find_handler(thd, table_list))) { - if (!(tmp=new Delayed_insert())) + if (!(di= new Delayed_insert())) { my_error(ER_OUTOFMEMORY,MYF(0),sizeof(Delayed_insert)); thd->fatal_error(); @@ -1837,28 +1870,30 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list) pthread_mutex_lock(&LOCK_thread_count); thread_count++; pthread_mutex_unlock(&LOCK_thread_count); - tmp->thd.set_db(table_list->db, strlen(table_list->db)); - tmp->thd.query= my_strdup(table_list->table_name,MYF(MY_WME)); - if (tmp->thd.db == NULL || tmp->thd.query == NULL) + di->thd.set_db(table_list->db, strlen(table_list->db)); + di->thd.query= my_strdup(table_list->table_name, MYF(MY_WME)); + if (di->thd.db == NULL || di->thd.query == NULL) { /* The error is reported */ - delete tmp; + delete di; thd->fatal_error(); goto end_create; } - tmp->table_list= *table_list; // Needed to open table - tmp->table_list.alias= tmp->table_list.table_name= tmp->thd.query; - tmp->lock(); - pthread_mutex_lock(&tmp->mutex); - if ((error=pthread_create(&tmp->thd.real_id,&connection_attrib, - handle_delayed_insert,(void*) tmp))) + di->table_list= *table_list; // Needed to open table + /* Replace volatile strings with local copies */ + di->table_list.alias= di->table_list.table_name= di->thd.query; + di->table_list.db= di->thd.db; + di->lock(); + pthread_mutex_lock(&di->mutex); + if ((error= pthread_create(&di->thd.real_id, &connection_attrib, + handle_delayed_insert, (void*) di))) { DBUG_PRINT("error", ("Can't create thread to handle delayed insert (error %d)", error)); - pthread_mutex_unlock(&tmp->mutex); - tmp->unlock(); - delete tmp; + pthread_mutex_unlock(&di->mutex); + di->unlock(); + delete di; my_error(ER_CANT_CREATE_THREAD, MYF(0), error); thd->fatal_error(); goto end_create; @@ -1866,15 +1901,15 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list) /* Wait until table is open */ thd->proc_info="waiting for handler open"; - while (!tmp->thd.killed && !tmp->table && !thd->killed) + while (!di->thd.killed && !di->table && !thd->killed) { - pthread_cond_wait(&tmp->cond_client,&tmp->mutex); + pthread_cond_wait(&di->cond_client, &di->mutex); } - pthread_mutex_unlock(&tmp->mutex); + pthread_mutex_unlock(&di->mutex); thd->proc_info="got old table"; - if (tmp->thd.killed) + if (di->thd.killed) { - if (tmp->thd.net.report_error) + if (di->thd.net.report_error) { /* Copy the error message. Note that we don't treat fatal @@ -1882,31 +1917,34 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list) main thread. Use of my_message will enable stored procedures continue handlers. */ - my_message(tmp->thd.net.last_errno, tmp->thd.net.last_error, + my_message(di->thd.net.last_errno, di->thd.net.last_error, MYF(0)); } - tmp->unlock(); + di->unlock(); goto end_create; } if (thd->killed) { - tmp->unlock(); + di->unlock(); goto end_create; } + pthread_mutex_lock(&LOCK_delayed_insert); + delayed_threads.append(di); + pthread_mutex_unlock(&LOCK_delayed_insert); } pthread_mutex_unlock(&LOCK_delayed_create); } - pthread_mutex_lock(&tmp->mutex); - table_list->table= tmp->get_local_table(thd); - pthread_mutex_unlock(&tmp->mutex); + pthread_mutex_lock(&di->mutex); + table_list->table= di->get_local_table(thd); + pthread_mutex_unlock(&di->mutex); if (table_list->table) { DBUG_ASSERT(thd->net.report_error == 0); - thd->di=tmp; + thd->di= di; } /* Unlock the delayed insert object after its last access. */ - tmp->unlock(); + di->unlock(); DBUG_RETURN((table_list->table == NULL)); end_create: @@ -1936,7 +1974,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) my_ptrdiff_t adjust_ptrs; Field **field,**org_field, *found_next_number_field; TABLE *copy; - TABLE_SHARE *share= table->s; + TABLE_SHARE *share; uchar *bitmap; DBUG_ENTER("Delayed_insert::get_local_table"); @@ -1960,6 +1998,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) goto error; } } + share= table->s; /* Allocate memory for the TABLE object, the field pointers array, and @@ -2155,26 +2194,26 @@ void kill_delayed_threads(void) VOID(pthread_mutex_lock(&LOCK_delayed_insert)); // For unlink from list I_List_iterator<Delayed_insert> it(delayed_threads); - Delayed_insert *tmp; - while ((tmp=it++)) + Delayed_insert *di; + while ((di= it++)) { - tmp->thd.killed= THD::KILL_CONNECTION; - if (tmp->thd.mysys_var) + di->thd.killed= THD::KILL_CONNECTION; + if (di->thd.mysys_var) { - pthread_mutex_lock(&tmp->thd.mysys_var->mutex); - if (tmp->thd.mysys_var->current_cond) + pthread_mutex_lock(&di->thd.mysys_var->mutex); + if (di->thd.mysys_var->current_cond) { /* We need the following test because the main mutex may be locked in handle_delayed_insert() */ - if (&tmp->mutex != tmp->thd.mysys_var->current_mutex) - pthread_mutex_lock(tmp->thd.mysys_var->current_mutex); - pthread_cond_broadcast(tmp->thd.mysys_var->current_cond); - if (&tmp->mutex != tmp->thd.mysys_var->current_mutex) - pthread_mutex_unlock(tmp->thd.mysys_var->current_mutex); + if (&di->mutex != di->thd.mysys_var->current_mutex) + pthread_mutex_lock(di->thd.mysys_var->current_mutex); + pthread_cond_broadcast(di->thd.mysys_var->current_cond); + if (&di->mutex != di->thd.mysys_var->current_mutex) + pthread_mutex_unlock(di->thd.mysys_var->current_mutex); } - pthread_mutex_unlock(&tmp->thd.mysys_var->mutex); + pthread_mutex_unlock(&di->thd.mysys_var->mutex); } } VOID(pthread_mutex_unlock(&LOCK_delayed_insert)); // For unlink from list @@ -2194,7 +2233,7 @@ pthread_handler_t handle_delayed_insert(void *arg) /* Add thread to THD list so that's it's visible in 'show processlist' */ pthread_mutex_lock(&LOCK_thread_count); thd->thread_id= thd->variables.pseudo_thread_id= thread_id++; - thd->end_time(); + thd->set_current_time(); threads.append(thd); thd->killed=abort_loop ? THD::KILL_CONNECTION : THD::NOT_KILLED; pthread_mutex_unlock(&LOCK_thread_count); @@ -2226,7 +2265,7 @@ pthread_handler_t handle_delayed_insert(void *arg) } /* open table */ - if (!(di->table=open_ltable(thd,&di->table_list,TL_WRITE_DELAYED))) + if (!(di->table=open_ltable(thd, &di->table_list, TL_WRITE_DELAYED, 0))) { thd->fatal_error(); // Abort waiting inserts goto err; @@ -2248,11 +2287,6 @@ pthread_handler_t handle_delayed_insert(void *arg) } di->table->copy_blobs=1; - /* One can now use this */ - pthread_mutex_lock(&LOCK_delayed_insert); - delayed_threads.append(di); - pthread_mutex_unlock(&LOCK_delayed_insert); - /* Tell client that the thread is initialized */ pthread_cond_signal(&di->cond_client); @@ -2896,7 +2930,6 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE); if (info.handle_duplicates == DUP_UPDATE) table->file->extra(HA_EXTRA_INSERT_WITH_UPDATE); - thd->no_trans_update.stmt= FALSE; thd->abort_on_warning= (!info.ignore && (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES | @@ -3045,6 +3078,7 @@ bool select_insert::send_eof() int error; bool const trans_table= table->file->has_transactions(); ulonglong id; + bool changed; DBUG_ENTER("select_insert::send_eof"); DBUG_PRINT("enter", ("trans_table=%d, table_type='%s'", trans_table, table->file->table_type())); @@ -3053,24 +3087,18 @@ bool select_insert::send_eof() table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); - if (info.copied || info.deleted || info.updated) + if (changed= (info.copied || info.deleted || info.updated)) { /* We must invalidate the table in the query cache before binlog writing and ha_autocommit_or_rollback. */ query_cache_invalidate3(thd, table, 1); - /* - Mark that we have done permanent changes if all of the below is true - - Table doesn't support transactions - - It's a normal (not temporary) table. (Changes to temporary tables - are not logged in RBR) - - We are using statement based replication - */ - if (!trans_table && - (!table->s->tmp_table || !thd->current_stmt_binlog_row_based)) - thd->no_trans_update.all= TRUE; - } + if (thd->transaction.stmt.modified_non_trans_table) + thd->transaction.all.modified_non_trans_table= TRUE; + } + DBUG_ASSERT(trans_table || !changed || + thd->transaction.stmt.modified_non_trans_table); /* Write to binlog before commiting transaction. No statement will @@ -3167,7 +3195,7 @@ void select_insert::abort() { table->file->has_transactions(), FALSE); if (!thd->current_stmt_binlog_row_based && !table->s->tmp_table && !can_rollback_data()) - thd->no_trans_update.all= TRUE; + thd->transaction.all.modified_non_trans_table= TRUE; query_cache_invalidate3(thd, table, 1); } } @@ -3460,10 +3488,12 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) /* If error during the CREATE SELECT we drop the table, so no need for - engines to do logging of insertions (optimization). + engines to do logging of insertions (optimization). We don't do it for + temporary tables (yet) as re-enabling causes an undesirable commit. */ - if (ha_enable_transaction(thd, FALSE)) - DBUG_RETURN(-1); + if (((thd->lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) == 0) && + ha_enable_transaction(thd, FALSE)) + DBUG_RETURN(-1); if (!(table= create_table_from_items(thd, create_info, create_table, alter_info, &values, @@ -3498,7 +3528,6 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) table->file->extra(HA_EXTRA_INSERT_WITH_UPDATE); if (!thd->prelocked_mode) table->file->ha_start_bulk_insert((ha_rows) 0); - thd->no_trans_update.stmt= FALSE; thd->abort_on_warning= (!info.ignore && (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES | @@ -3536,13 +3565,13 @@ select_create::binlog_show_create_table(TABLE **tables, uint count) char buf[2048]; String query(buf, sizeof(buf), system_charset_info); int result; - TABLE_LIST table_list; + TABLE_LIST tmp_table_list; - memset(&table_list, 0, sizeof(table_list)); - table_list.table = *tables; + memset(&tmp_table_list, 0, sizeof(tmp_table_list)); + tmp_table_list.table = *tables; query.length(0); // Have to zero it since constructor doesn't - result= store_create_info(thd, &table_list, &query, create_info); + result= store_create_info(thd, &tmp_table_list, &query, create_info); DBUG_ASSERT(result == 0); /* store_create_info() always return 0 */ thd->binlog_query(THD::STMT_QUERY_TYPE, @@ -3605,11 +3634,12 @@ bool select_create::send_eof() nevertheless. */ if (!table->s->tmp_table) + { + ha_enable_transaction(thd, TRUE); 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); - ha_enable_transaction(thd, TRUE); if (thd->extra_lock) { mysql_unlock_tables(thd, thd->extra_lock); @@ -3632,6 +3662,9 @@ void select_create::abort() select_insert::abort(); reenable_binlog(thd); + if (table && !table->s->tmp_table) + ha_enable_transaction(thd, TRUE); + /* We roll back the statement, including truncating the transaction cache of the binary log, if the statement failed. @@ -3648,8 +3681,6 @@ void select_create::abort() if (thd->current_stmt_binlog_row_based) ha_rollback_stmt(thd); - ha_enable_transaction(thd, TRUE); - if (thd->extra_lock) { mysql_unlock_tables(thd, thd->extra_lock); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 639f0d2325d..0a5f83af400 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -124,7 +124,7 @@ Lex_input_stream::Lex_input_stream(THD *thd, m_tok_start_prev(NULL), m_buf(buffer), m_buf_length(length), - m_echo(true), + m_echo(TRUE), m_cpp_tok_start(NULL), m_cpp_tok_start_prev(NULL), m_cpp_tok_end(NULL), @@ -173,7 +173,7 @@ void Lex_input_stream::body_utf8_start(THD *thd, const char *begin_ptr) } /** - The operation appends unprocessed part of pre-processed buffer till + @brief The operation appends unprocessed part of pre-processed buffer till the given pointer (ptr) and sets m_cpp_utf8_processed_ptr to end_ptr. The idea is that some tokens in the pre-processed buffer (like character @@ -323,7 +323,6 @@ void lex_start(THD *thd) lex->length=0; lex->part_info= 0; lex->select_lex.in_sum_expr=0; - lex->select_lex.expr_list.empty(); lex->select_lex.ftfunc_list_alloc.empty(); lex->select_lex.ftfunc_list= &lex->select_lex.ftfunc_list_alloc; lex->select_lex.group_list.empty(); @@ -498,10 +497,12 @@ static char *get_text(Lex_input_stream *lip, int pre_skip, int post_skip) uint found_escape=0; CHARSET_INFO *cs= lip->m_thd->charset(); + lip->tok_bitmap= 0; sep= lip->yyGetLast(); // String should end with this while (! lip->eof()) { c= lip->yyGet(); + lip->tok_bitmap|= c; #ifdef USE_MB { int l; @@ -718,6 +719,7 @@ static inline uint int_token(const char *str,uint length) int MYSQLlex(void *arg, void *yythd) { reg1 uchar c; + bool comment_closed; int tokval, result_state; uint length; enum my_lex_states state; @@ -815,6 +817,7 @@ int MYSQLlex(void *arg, void *yythd) break; } yylval->lex_str.length= lip->yytoklen; + lex->text_string_is_7bit= (lip->tok_bitmap & 0x80) ? 0 : 1; return(NCHAR_STRING); case MY_LEX_IDENT_OR_HEX: @@ -1178,6 +1181,7 @@ int MYSQLlex(void *arg, void *yythd) lip->m_underscore_cs= NULL; + lex->text_string_is_7bit= (lip->tok_bitmap & 0x80) ? 0 : 1; return(TEXT_STRING); case MY_LEX_COMMENT: // Comment @@ -1200,7 +1204,7 @@ int MYSQLlex(void *arg, void *yythd) { lip->in_comment= DISCARD_COMMENT; /* Accept '/' '*' '!', but do not keep this marker. */ - lip->set_echo(false); + lip->set_echo(FALSE); lip->yySkip(); lip->yySkip(); lip->yySkip(); @@ -1208,7 +1212,10 @@ int MYSQLlex(void *arg, void *yythd) /* The special comment format is very strict: '/' '*' '!', followed by exactly - 2 digits (major), then 3 digits (minor). + 1 digit (major), 2 digits (minor), then 2 digits (dot). + 32302 -> 3.23.02 + 50032 -> 5.0.32 + 50114 -> 5.1.14 */ char version_str[6]; version_str[0]= lip->yyPeekn(0); @@ -1227,13 +1234,13 @@ int MYSQLlex(void *arg, void *yythd) ulong version; version=strtol(version_str, NULL, 10); - /* Accept 'M' 'M' 'm' 'm' 'm' */ + /* Accept 'M' 'm' 'm' 'd' 'd' */ lip->yySkipn(5); if (version <= MYSQL_VERSION_ID) { /* Expand the content of the special comment as real code */ - lip->set_echo(true); + lip->set_echo(TRUE); state=MY_LEX_START; break; } @@ -1241,7 +1248,7 @@ int MYSQLlex(void *arg, void *yythd) else { state=MY_LEX_START; - lip->set_echo(true); + lip->set_echo(TRUE); break; } } @@ -1251,17 +1258,37 @@ int MYSQLlex(void *arg, void *yythd) lip->yySkip(); // Accept / lip->yySkip(); // Accept * } - - while (! lip->eof() && - ((c=lip->yyGet()) != '*' || lip->yyPeek() != '/')) + /* + Discard: + - regular '/' '*' comments, + - special comments '/' '*' '!' for a future version, + by scanning until we find a closing '*' '/' marker. + Note: There is no such thing as nesting comments, + the first '*' '/' sequence seen will mark the end. + */ + comment_closed= FALSE; + while (! lip->eof()) { - if (c == '\n') + c= lip->yyGet(); + if (c == '*') + { + if (lip->yyPeek() == '/') + { + lip->yySkip(); + comment_closed= TRUE; + state = MY_LEX_START; + break; + } + } + else if (c == '\n') lip->yylineno++; } - if (! lip->eof()) - lip->yySkip(); // remove last '/' + /* Unbalanced comments with a missing '*' '/' are a syntax error */ + if (! comment_closed) + return (ABORT_SYM); state = MY_LEX_START; // Try again - lip->set_echo(true); + lip->in_comment= NO_COMMENT; + lip->set_echo(TRUE); break; case MY_LEX_END_LONG_COMMENT: if ((lip->in_comment != NO_COMMENT) && lip->yyPeek() == '/') @@ -1272,7 +1299,7 @@ int MYSQLlex(void *arg, void *yythd) lip->set_echo(lip->in_comment == PRESERVE_COMMENT); lip->yySkipn(2); /* And start recording the tokens again */ - lip->set_echo(true); + lip->set_echo(TRUE); lip->in_comment=NO_COMMENT; state=MY_LEX_START; } @@ -1297,7 +1324,7 @@ int MYSQLlex(void *arg, void *yythd) lip->found_semicolon= lip->get_ptr(); thd->server_status|= SERVER_MORE_RESULTS_EXISTS; lip->next_state= MY_LEX_END; - lip->set_echo(true); + lip->set_echo(TRUE); return (END_OF_INPUT); } state= MY_LEX_CHAR; // Return ';' @@ -1309,9 +1336,12 @@ int MYSQLlex(void *arg, void *yythd) if (lip->eof()) { lip->yyUnget(); // Reject the last '\0' - lip->set_echo(false); + lip->set_echo(FALSE); lip->yySkip(); - lip->set_echo(true); + lip->set_echo(TRUE); + /* Unbalanced comments with a missing '*' '/' are a syntax error */ + if (lip->in_comment != NO_COMMENT) + return (ABORT_SYM); lip->next_state=MY_LEX_END; // Mark for next loop return(END_OF_INPUT); } @@ -1396,6 +1426,19 @@ int MYSQLlex(void *arg, void *yythd) } +/** + Construct a copy of this object to be used for mysql_alter_table + and mysql_create_table. + + Historically, these two functions modify their Alter_info + arguments. This behaviour breaks re-execution of prepared + statements and stored procedures and is compensated by always + supplying a copy of Alter_info to these functions. + + @return You need to use check the error in THD for out + of memory condition after calling this function. +*/ + Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root) :drop_list(rhs.drop_list, mem_root), alter_list(rhs.alter_list, mem_root), @@ -1513,6 +1556,7 @@ void st_select_lex::init_query() */ parent_lex->push_context(&context); cond_count= between_count= with_wild= 0; + max_equal_elems= 0; conds_processed_with_permanent_arena= 0; ref_pointer_array= 0; select_n_where_fields= 0; @@ -1538,7 +1582,6 @@ void st_select_lex::init_select() options= 0; sql_cache= SQL_CACHE_UNSPECIFIED; braces= 0; - expr_list.empty(); interval_list.empty(); ftfunc_list_alloc.empty(); inner_sum_func_list= 0; @@ -1733,7 +1776,7 @@ void st_select_lex_unit::exclude_tree() 'last' should be reachable from this st_select_lex_node */ -void st_select_lex::mark_as_dependent(SELECT_LEX *last) +void st_select_lex::mark_as_dependent(st_select_lex *last) { /* Mark all selects from resolved to 1 before select where was @@ -1770,7 +1813,7 @@ TABLE_LIST *st_select_lex_node::add_table_to_list (THD *thd, Table_ident *table, LEX_STRING *alias, ulong table_join_options, thr_lock_type flags, - List<index_hint> *hints, + List<Index_hint> *hints, LEX_STRING *option) { return 0; @@ -2297,7 +2340,7 @@ bool st_lex::need_correct_ident() VIEW_CHECK_CASCADED CHECK OPTION CASCADED */ -uint8 st_lex::get_effective_with_check(st_table_list *view) +uint8 st_lex::get_effective_with_check(TABLE_LIST *view) { if (view->select_lex->master_unit() == &unit && which_check_option_applicable()) @@ -2306,6 +2349,43 @@ uint8 st_lex::get_effective_with_check(st_table_list *view) } +/** + This method should be called only during parsing. + It is aware of compound statements (stored routine bodies) + and will initialize the destination with the default + database of the stored routine, rather than the default + database of the connection it is parsed in. + E.g. if one has no current database selected, or current database + set to 'bar' and then issues: + + CREATE PROCEDURE foo.p1() BEGIN SELECT * FROM t1 END// + + t1 is meant to refer to foo.t1, not to bar.t1. + + This method is needed to support this rule. + + @return TRUE in case of error (parsing should be aborted, FALSE in + case of success +*/ + +bool +st_lex::copy_db_to(char **p_db, size_t *p_db_length) const +{ + if (sphead) + { + DBUG_ASSERT(sphead->m_db.str && sphead->m_db.length); + /* + It is safe to assign the string by-pointer, both sphead and + its statements reside in the same memory root. + */ + *p_db= sphead->m_db.str; + if (p_db_length) + *p_db_length= sphead->m_db.length; + return FALSE; + } + return thd->copy_db_to(p_db, p_db_length); +} + /* initialize limit counters @@ -2314,13 +2394,22 @@ uint8 st_lex::get_effective_with_check(st_table_list *view) values - SELECT_LEX with initial values for counters */ -void st_select_lex_unit::set_limit(SELECT_LEX *sl) +void st_select_lex_unit::set_limit(st_select_lex *sl) { ha_rows select_limit_val; + ulonglong val; DBUG_ASSERT(! thd->stmt_arena->is_stmt_prepare()); - select_limit_val= (ha_rows)(sl->select_limit ? sl->select_limit->val_uint() : - HA_POS_ERROR); + val= sl->select_limit ? sl->select_limit->val_uint() : HA_POS_ERROR; + select_limit_val= (ha_rows)val; +#ifndef BIG_TABLES + /* + Check for overflow : ha_rows can be smaller then ulonglong if + BIG_TABLES is off. + */ + if (val != (ulonglong)select_limit_val) + select_limit_val= HA_POS_ERROR; +#endif offset_limit_cnt= (ha_rows)(sl->offset_limit ? sl->offset_limit->val_uint() : ULL(0)); select_limit_cnt= select_limit_val + offset_limit_cnt; @@ -2329,6 +2418,154 @@ void st_select_lex_unit::set_limit(SELECT_LEX *sl) } +/** + @brief Set the initial purpose of this TABLE_LIST object in the list of used + tables. + + We need to track this information on table-by-table basis, since when this + table becomes an element of the pre-locked list, it's impossible to identify + which SQL sub-statement it has been originally used in. + + E.g.: + + User request: SELECT * FROM t1 WHERE f1(); + FUNCTION f1(): DELETE FROM t2; RETURN 1; + BEFORE DELETE trigger on t2: INSERT INTO t3 VALUES (old.a); + + For this user request, the pre-locked list will contain t1, t2, t3 + table elements, each needed for different DML. + + The trigger event map is updated to reflect INSERT, UPDATE, DELETE, + REPLACE, LOAD DATA, CREATE TABLE .. SELECT, CREATE TABLE .. + REPLACE SELECT statements, and additionally ON DUPLICATE KEY UPDATE + clause. +*/ + +void st_lex::set_trg_event_type_for_tables() +{ + uint8 new_trg_event_map= 0; + + /* + Some auxiliary operations + (e.g. GRANT processing) create TABLE_LIST instances outside + the parser. Additionally, some commands (e.g. OPTIMIZE) change + the lock type for a table only after parsing is done. Luckily, + these do not fire triggers and do not need to pre-load them. + For these TABLE_LISTs set_trg_event_type is never called, and + trg_event_map is always empty. That means that the pre-locking + algorithm will ignore triggers defined on these tables, if + any, and the execution will either fail with an assert in + sql_trigger.cc or with an error that a used table was not + pre-locked, in case of a production build. + + TODO: this usage pattern creates unnecessary module dependencies + and should be rewritten to go through the parser. + Table list instances created outside the parser in most cases + refer to mysql.* system tables. It is not allowed to have + a trigger on a system table, but keeping track of + initialization provides extra safety in case this limitation + is circumvented. + */ + + switch (sql_command) { + case SQLCOM_LOCK_TABLES: + /* + On a LOCK TABLE, all triggers must be pre-loaded for this TABLE_LIST + when opening an associated TABLE. + */ + new_trg_event_map= static_cast<uint8> + (1 << static_cast<int>(TRG_EVENT_INSERT)) | + static_cast<uint8> + (1 << static_cast<int>(TRG_EVENT_UPDATE)) | + static_cast<uint8> + (1 << static_cast<int>(TRG_EVENT_DELETE)); + break; + /* + Basic INSERT. If there is an additional ON DUPLIATE KEY UPDATE + clause, it will be handled later in this method. + */ + case SQLCOM_INSERT: /* fall through */ + case SQLCOM_INSERT_SELECT: + /* + LOAD DATA ... INFILE is expected to fire BEFORE/AFTER INSERT + triggers. + If the statement also has REPLACE clause, it will be + handled later in this method. + */ + case SQLCOM_LOAD: /* fall through */ + /* + REPLACE is semantically equivalent to INSERT. In case + of a primary or unique key conflict, it deletes the old + record and inserts a new one. So we also may need to + fire ON DELETE triggers. This functionality is handled + later in this method. + */ + case SQLCOM_REPLACE: /* fall through */ + case SQLCOM_REPLACE_SELECT: + /* + CREATE TABLE ... SELECT defaults to INSERT if the table or + view already exists. REPLACE option of CREATE TABLE ... + REPLACE SELECT is handled later in this method. + */ + case SQLCOM_CREATE_TABLE: + new_trg_event_map|= static_cast<uint8> + (1 << static_cast<int>(TRG_EVENT_INSERT)); + break; + /* Basic update and multi-update */ + case SQLCOM_UPDATE: /* fall through */ + case SQLCOM_UPDATE_MULTI: + new_trg_event_map|= static_cast<uint8> + (1 << static_cast<int>(TRG_EVENT_UPDATE)); + break; + /* Basic delete and multi-delete */ + case SQLCOM_DELETE: /* fall through */ + case SQLCOM_DELETE_MULTI: + new_trg_event_map|= static_cast<uint8> + (1 << static_cast<int>(TRG_EVENT_DELETE)); + break; + default: + break; + } + + switch (duplicates) { + case DUP_UPDATE: + new_trg_event_map|= static_cast<uint8> + (1 << static_cast<int>(TRG_EVENT_UPDATE)); + break; + case DUP_REPLACE: + new_trg_event_map|= static_cast<uint8> + (1 << static_cast<int>(TRG_EVENT_DELETE)); + break; + case DUP_ERROR: + default: + break; + } + + + /* + Do not iterate over sub-selects, only the tables in the outermost + SELECT_LEX can be modified, if any. + */ + TABLE_LIST *tables= select_lex.get_table_list(); + + while (tables) + { + /* + This is a fast check to filter out statements that do + not change data, or tables on the right side, in case of + INSERT .. SELECT, CREATE TABLE .. SELECT and so on. + Here we also filter out OPTIMIZE statement and non-updateable + views, for which lock_type is TL_UNLOCK or TL_READ after + parsing. + */ + if (static_cast<int>(tables->lock_type) >= + static_cast<int>(TL_WRITE_ALLOW_WRITE)) + tables->trg_event_map= new_trg_event_map; + tables= tables->next_local; + } +} + + /* Unlink the first table from the global table list and the first table from outer select (lex->select_lex) local list @@ -2637,8 +2874,8 @@ void st_select_lex::fix_prepare_information(THD *thd, Item **conds, SYNOPSIS set_index_hint_type() - type the kind of hints to be added from now on. - clause the clause to use for hints to be added from now on. + type_arg The kind of hints to be added from now on. + clause The clause to use for hints to be added from now on. DESCRIPTION Used in filling up the tagged hints list. @@ -2647,10 +2884,10 @@ void st_select_lex::fix_prepare_information(THD *thd, Item **conds, Then the context variable index_hint_type can be reset to the next hint type. */ -void st_select_lex::set_index_hint_type(enum index_hint_type type, +void st_select_lex::set_index_hint_type(enum index_hint_type type_arg, index_clause_map clause) { - current_index_hint_type= type; + current_index_hint_type= type_arg; current_index_hint_clause= clause; } @@ -2665,7 +2902,7 @@ void st_select_lex::set_index_hint_type(enum index_hint_type type, void st_select_lex::alloc_index_hints (THD *thd) { - index_hints= new (thd->mem_root) List<index_hint>(); + index_hints= new (thd->mem_root) List<Index_hint>(); } @@ -2686,7 +2923,7 @@ void st_select_lex::alloc_index_hints (THD *thd) bool st_select_lex::add_index_hint (THD *thd, char *str, uint length) { return index_hints->push_front (new (thd->mem_root) - index_hint(current_index_hint_type, + Index_hint(current_index_hint_type, current_index_hint_clause, str, length)); } @@ -2696,7 +2933,7 @@ bool st_select_lex::add_index_hint (THD *thd, char *str, uint length) partitioning or if only partitions to add or to split. @note This needs to be outside of WITH_PARTITION_STORAGE_ENGINE since it - is used from the sql parser that doesn't have any #ifdef's + is used from the sql parser that doesn't have any ifdef's @retval TRUE Yes, it is part of a management partition command @retval FALSE No, not a management partition command diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 254403fe736..08104769704 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -13,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 */ +/** + @defgroup Semantic_Analysis Semantic Analysis +*/ /* YACC and LEX Definitions */ @@ -236,7 +239,7 @@ typedef uchar index_clause_map; INDEX_HINT_MASK_ORDER) /* Single element of an USE/FORCE/IGNORE INDEX list specified as a SQL hint */ -class index_hint : public Sql_alloc +class Index_hint : public Sql_alloc { public: /* The type of the hint : USE/FORCE/IGNORE */ @@ -249,7 +252,7 @@ public: */ LEX_STRING key_name; - index_hint (enum index_hint_type type_arg, index_clause_map clause_arg, + Index_hint (enum index_hint_type type_arg, index_clause_map clause_arg, char *str, uint length) : type(type_arg), clause(clause_arg) { @@ -441,7 +444,7 @@ public: LEX_STRING *alias, ulong table_options, thr_lock_type flags= TL_UNLOCK, - List<index_hint> *hints= 0, + List<Index_hint> *hints= 0, LEX_STRING *option= 0); virtual void set_lock_for_tables(thr_lock_type lock_type) {} @@ -593,7 +596,6 @@ public: const char *type; /* type of select for EXPLAIN */ SQL_LIST order_list; /* ORDER clause */ - List<List_item> expr_list; SQL_LIST *gorder_list; Item *select_limit, *offset_limit; /* LIMIT clause parameters */ // Arrays of pointers to top elements of all_fields list @@ -606,7 +608,8 @@ 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 */ + uint between_count; /* number of between predicates in where/having/on */ + uint max_equal_elems; /* maximal number of elements in multiple equalities */ /* Number of fields used in select list or where clause of current select and all inner subselects. @@ -719,7 +722,7 @@ public: LEX_STRING *alias, ulong table_options, thr_lock_type flags= TL_UNLOCK, - List<index_hint> *hints= 0, + List<Index_hint> *hints= 0, LEX_STRING *option= 0); TABLE_LIST* get_table_list(); bool init_nested_join(THD *thd); @@ -779,9 +782,9 @@ public: /* make a list to hold index hints */ void alloc_index_hints (THD *thd); /* read and clear the index hints */ - List<index_hint>* pop_index_hints(void) + List<Index_hint>* pop_index_hints(void) { - List<index_hint> *hints= index_hints; + List<Index_hint> *hints= index_hints; index_hints= NULL; return hints; } @@ -793,7 +796,7 @@ private: enum index_hint_type current_index_hint_type; index_clause_map current_index_hint_clause; /* a list of USE/FORCE/IGNORE INDEX */ - List<index_hint> *index_hints; + List<Index_hint> *index_hints; }; typedef class st_select_lex SELECT_LEX; @@ -859,8 +862,8 @@ public: uint no_parts; enum_alter_table_change_level change_level; Create_field *datetime_field; - bool error_if_not_empty; - + bool error_if_not_empty; + Alter_info() : flags(0), @@ -887,16 +890,6 @@ public: datetime_field= 0; error_if_not_empty= FALSE; } - /** - Construct a copy of this object to be used for mysql_alter_table - and mysql_create_table. Historically, these two functions modify - their Alter_info arguments. This behaviour breaks re-execution of - prepared statements and stored procedures and is compensated by - always supplying a copy of Alter_info to these functions. - - @return You need to use check the error in THD for out - of memory condition after calling this function. - */ Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root); private: Alter_info &operator=(const Alter_info &rhs); // not implemented @@ -1102,8 +1095,9 @@ enum enum_comment_state /** - This class represents the character input stream consumed during + @brief This class represents the character input stream consumed during lexical analysis. + In addition to consuming the input stream, this class performs some comment pre processing, by filtering out out of bound special text from the query input stream. @@ -1113,6 +1107,7 @@ enum enum_comment_state is the pre-processed buffer that contains only the query text that should be seen once out-of-bound data is removed. */ + class Lex_input_stream { public: @@ -1121,6 +1116,7 @@ public: /** Set the echo mode. + When echo is true, characters parsed from the raw input stream are preserved. When false, characters parsed are silently ignored. @param echo the echo mode. @@ -1239,19 +1235,19 @@ public: } /** Get the raw query buffer. */ - const char* get_buf() + const char *get_buf() { return m_buf; } /** Get the pre-processed query buffer. */ - const char* get_cpp_buf() + const char *get_cpp_buf() { return m_cpp_buf; } /** Get the end of the raw query buffer. */ - const char* get_end_of_query() + const char *get_end_of_query() { return m_end_of_query; } @@ -1279,43 +1275,43 @@ public: } /** Get the token start position, in the raw buffer. */ - const char* get_tok_start() + const char *get_tok_start() { return m_tok_start; } /** Get the token start position, in the pre-processed buffer. */ - const char* get_cpp_tok_start() + const char *get_cpp_tok_start() { return m_cpp_tok_start; } /** Get the token end position, in the raw buffer. */ - const char* get_tok_end() + const char *get_tok_end() { return m_tok_end; } /** Get the token end position, in the pre-processed buffer. */ - const char* get_cpp_tok_end() + const char *get_cpp_tok_end() { return m_cpp_tok_end; } /** Get the previous token start position, in the raw buffer. */ - const char* get_tok_start_prev() + const char *get_tok_start_prev() { return m_tok_start_prev; } /** Get the current stream pointer, in the raw buffer. */ - const char* get_ptr() + const char *get_ptr() { return m_ptr; } /** Get the current stream pointer, in the pre-processed buffer. */ - const char* get_cpp_ptr() + const char *get_cpp_ptr() { return m_cpp_ptr; } @@ -1365,22 +1361,22 @@ public: private: /** Pointer to the current position in the raw input stream. */ - const char* m_ptr; + const char *m_ptr; /** Starting position of the last token parsed, in the raw buffer. */ - const char* m_tok_start; + const char *m_tok_start; /** Ending position of the previous token parsed, in the raw buffer. */ - const char* m_tok_end; + const char *m_tok_end; /** End of the query text in the input stream, in the raw buffer. */ - const char* m_end_of_query; + const char *m_end_of_query; /** Starting position of the previous token parsed, in the raw buffer. */ - const char* m_tok_start_prev; + const char *m_tok_start_prev; /** Begining of the query text in the input stream, in the raw buffer. */ - const char* m_buf; + const char *m_buf; /** Length of the raw buffer. */ uint m_buf_length; @@ -1389,28 +1385,28 @@ private: bool m_echo; /** Pre-processed buffer. */ - char* m_cpp_buf; + char *m_cpp_buf; /** Pointer to the current position in the pre-processed input stream. */ - char* m_cpp_ptr; + char *m_cpp_ptr; /** Starting position of the last token parsed, in the pre-processed buffer. */ - const char* m_cpp_tok_start; + const char *m_cpp_tok_start; /** Starting position of the previous token parsed, in the pre-procedded buffer. */ - const char* m_cpp_tok_start_prev; + const char *m_cpp_tok_start_prev; /** Ending position of the previous token parsed, in the pre-processed buffer. */ - const char* m_cpp_tok_end; + const char *m_cpp_tok_end; /** UTF8-body buffer created during parsing. */ char *m_body_utf8; @@ -1433,7 +1429,10 @@ public: Position of ';' in the stream, to delimit multiple queries. This delimiter is in the raw buffer. */ - const char* found_semicolon; + const char *found_semicolon; + + /** Token character bitmaps, to detect 7bit strings. */ + uchar tok_bitmap; /** SQL_MODE = IGNORE_SPACE. */ bool ignore_space; @@ -1502,8 +1501,9 @@ typedef struct st_lex : public Query_tables_list /* maintain a list of used plugins for this LEX */ DYNAMIC_ARRAY plugins; plugin_ref plugins_static_buffer[INITIAL_LEX_PLUGIN_LIST_SIZE]; - + CHARSET_INFO *charset; + bool text_string_is_7bit; /* store original leaf_tables for INSERT SELECT and PS/SP */ TABLE_LIST *leaf_tables_insert; @@ -1512,9 +1512,9 @@ typedef struct st_lex : public Query_tables_list /** End of SELECT of CREATE VIEW statement */ const char* create_view_select_end; - /** Start of 'ON <table>', in trigger statements. */ + /** Start of 'ON table', in trigger statements. */ const char* raw_trg_on_table_name_begin; - /** End of 'ON <table>', in trigger statements. */ + /** End of 'ON table', in trigger statements. */ const char* raw_trg_on_table_name_end; /* Partition info structure filled in by PARTITION BY parse part */ @@ -1732,6 +1732,8 @@ typedef struct st_lex : public Query_tables_list un->uncacheable|= cause; } } + void set_trg_event_type_for_tables(); + TABLE_LIST *unlink_first_table(bool *link_to_local); void link_first_table_back(TABLE_LIST *first, bool link_to_local); void first_lists_tables_same(); @@ -1741,7 +1743,7 @@ typedef struct st_lex : public Query_tables_list bool can_not_use_merged(); bool only_view_structure(); bool need_correct_ident(); - uint8 get_effective_with_check(st_table_list *view); + uint8 get_effective_with_check(TABLE_LIST *view); /* Is this update command where 'WHITH CHECK OPTION' clause is important @@ -1780,6 +1782,8 @@ typedef struct st_lex : public Query_tables_list context_stack.pop(); } + bool copy_db_to(char **p_db, size_t *p_db_length) const; + Name_resolution_context *current_context() { return context_stack.head(); @@ -1794,6 +1798,28 @@ typedef struct st_lex : public Query_tables_list bool table_or_sp_used(); bool is_partition_management() const; + + /** + @brief check if the statement is a single-level join + @return result of the check + @retval TRUE The statement doesn't contain subqueries, unions and + stored procedure calls. + @retval FALSE There are subqueries, UNIONs or stored procedure calls. + */ + bool is_single_level_stmt() + { + /* + This check exploits the fact that the last added to all_select_list is + on its top. So select_lex (as the first added) will be at the tail + of the list. + */ + if (&select_lex == all_selects_list && !sroutines.records) + { + DBUG_ASSERT(!all_selects_list->next_select_in_list()); + return TRUE; + } + return FALSE; + } } LEX; struct st_lex_local: public st_lex @@ -1822,4 +1848,8 @@ extern void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str); extern bool is_lex_native_function(const LEX_STRING *name); +/** + @} (End of group Semantic_Analysis) +*/ + #endif /* MYSQL_SERVER */ diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 0138030487b..8bbe1e413b3 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -376,7 +376,6 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, table->file->ha_start_bulk_insert((ha_rows) 0); table->copy_blobs=1; - thd->no_trans_update.stmt= FALSE; thd->abort_on_warning= (!ignore && (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES | @@ -410,7 +409,6 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ha_autocommit_... */ query_cache_invalidate3(thd, table_list, 0); - if (error) { if (read_file_from_client) @@ -469,8 +467,8 @@ 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); - if (!transactional_table) - thd->no_trans_update.all= TRUE; + if (thd->transaction.stmt.modified_non_trans_table) + thd->transaction.all.modified_non_trans_table= TRUE; #ifndef EMBEDDED_LIBRARY if (mysql_bin_log.is_open()) { @@ -506,6 +504,8 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, /* ok to client sent only after binlog write and engine commit */ send_ok(thd, info.copied + info.deleted, 0L, name); err: + DBUG_ASSERT(transactional_table || !(info.copied || info.deleted) || + thd->transaction.stmt.modified_non_trans_table); table->file->ha_release_auto_increment(); if (thd->lock) { @@ -552,7 +552,7 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, Item_field *sql_field; TABLE *table= table_list->table; ulonglong id; - bool no_trans_update_stmt, err; + bool err; DBUG_ENTER("read_fixed_length"); id= 0; @@ -580,7 +580,6 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, #ifdef HAVE_purify read_info.row_end[0]=0; #endif - no_trans_update_stmt= !table->file->has_transactions(); restore_record(table, s->default_values); /* @@ -650,7 +649,6 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, table->auto_increment_field_not_null= FALSE; if (err) DBUG_RETURN(1); - thd->no_trans_update.stmt= no_trans_update_stmt; /* We don't need to reset auto-increment field since we are restoring @@ -685,12 +683,11 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, TABLE *table= table_list->table; uint enclosed_length; ulonglong id; - bool no_trans_update_stmt, err; + bool err; DBUG_ENTER("read_sep_field"); enclosed_length=enclosed.length(); id= 0; - no_trans_update_stmt= !table->file->has_transactions(); for (;;it.rewind()) { @@ -827,7 +824,6 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, We don't need to reset auto-increment field since we are restoring its default value at the beginning of each loop iteration. */ - thd->no_trans_update.stmt= no_trans_update_stmt; if (read_info.next_line()) // Skip to next line break; if (read_info.line_cuted) @@ -851,6 +847,7 @@ continue_loop:; char READ_INFO::unescape(char chr) { + /* keep this switch synchornous with the ESCAPE_CHARS macro */ switch(chr) { case 'n': return '\n'; case 't': return '\t'; diff --git a/sql/sql_map.cc b/sql/sql_map.cc index 7b707f813fc..55f9b08d3fe 100644 --- a/sql/sql_map.cc +++ b/sql/sql_map.cc @@ -37,7 +37,7 @@ mapped_files::mapped_files(const char * filename,uchar *magic,uint magic_length) struct stat stat_buf; if (!fstat(file,&stat_buf)) { - if (!(map=(uchar*) my_mmap(0,(size=(ulong) stat_buf.st_size),PROT_READ, + if (!(map=(uchar*) my_mmap(0,(size_t)(size= stat_buf.st_size),PROT_READ, MAP_SHARED | MAP_NORESERVE,file, 0L))) { @@ -48,7 +48,7 @@ mapped_files::mapped_files(const char * filename,uchar *magic,uint magic_length) if (map && memcmp(map,magic,magic_length)) { my_error(ER_WRONG_MAGIC, MYF(0), name); - VOID(my_munmap((char*) map,size)); + VOID(my_munmap((char*) map,(size_t)size)); map=0; } if (!map) @@ -66,7 +66,7 @@ mapped_files::~mapped_files() #ifdef HAVE_MMAP if (file >= 0) { - VOID(my_munmap((char*) map,size)); + VOID(my_munmap((char*) map,(size_t)size)); VOID(my_close(file,MYF(0))); file= -1; map=0; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a49f560d8d4..e440aeaa588 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -28,6 +28,11 @@ #include "events.h" #include "sql_trigger.h" +/** + @defgroup Runtime_Environment Runtime Environment + @{ +*/ + /* Used in error handling only */ #define SP_TYPE_STRING(LP) \ ((LP)->sphead->m_type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE") @@ -120,7 +125,7 @@ bool end_active_trans(THD *thd) error=1; } thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); - thd->no_trans_update.all= FALSE; + thd->transaction.all.modified_non_trans_table= FALSE; DBUG_RETURN(error); } @@ -197,8 +202,8 @@ void init_update_queries(void) sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND; + sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND; sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA; @@ -211,7 +216,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_DROP_VIEW]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_CREATE_EVENT]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_ALTER_EVENT]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_DROP_EVENT]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_DROP_EVENT]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_UPDATE]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT; sql_command_flags[SQLCOM_UPDATE_MULTI]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT; @@ -223,7 +228,6 @@ void init_update_queries(void) sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT; sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_STATUS_FUNC]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_STATUS]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_DATABASES]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_TRIGGERS]= CF_STATUS_COMMAND; @@ -235,10 +239,36 @@ void init_update_queries(void) sql_command_flags[SQLCOM_SHOW_VARIABLES]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_CHARSETS]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_COLLATIONS]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND; - - sql_command_flags[SQLCOM_SHOW_TABLES]= (CF_STATUS_COMMAND | - CF_SHOW_TABLE_COMMAND); + sql_command_flags[SQLCOM_SHOW_NEW_MASTER]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_BINLOGS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_SLAVE_HOSTS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_BINLOG_EVENTS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_COLUMN_TYPES]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_STORAGE_ENGINES]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_AUTHORS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CONTRIBUTORS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_PRIVILEGES]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_WARNS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_ERRORS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_ENGINE_STATUS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_ENGINE_MUTEX]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_ENGINE_LOGS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_PROCESSLIST]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_GRANTS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CREATE_DB]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CREATE]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_MASTER_STAT]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_SLAVE_STAT]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CREATE_PROC]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CREATE_FUNC]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CREATE_TRIGGER]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_STATUS_FUNC]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_PROC_CODE]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_FUNC_CODE]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CREATE_EVENT]= CF_STATUS_COMMAND; + + sql_command_flags[SQLCOM_SHOW_TABLES]= (CF_STATUS_COMMAND | + CF_SHOW_TABLE_COMMAND); sql_command_flags[SQLCOM_SHOW_TABLE_STATUS]= (CF_STATUS_COMMAND | CF_SHOW_TABLE_COMMAND); @@ -250,6 +280,14 @@ void init_update_queries(void) */ sql_command_flags[SQLCOM_CALL]= CF_HAS_ROW_COUNT; sql_command_flags[SQLCOM_EXECUTE]= CF_HAS_ROW_COUNT; + + /* + The following admin table operations are allowed + on log tables. + */ + sql_command_flags[SQLCOM_REPAIR]= CF_WRITE_LOGS_COMMAND; + sql_command_flags[SQLCOM_OPTIMIZE]= CF_WRITE_LOGS_COMMAND; + sql_command_flags[SQLCOM_ANALYZE]= CF_WRITE_LOGS_COMMAND; } @@ -259,6 +297,17 @@ bool is_update_query(enum enum_sql_command command) return (sql_command_flags[command] & CF_CHANGES_DATA) != 0; } +/** + Check if a sql command is allowed to write to log tables. + @param command The SQL command + @return true if writing is allowed +*/ +bool is_log_table_write_query(enum enum_sql_command command) +{ + DBUG_ASSERT(command >= 0 && command <= SQLCOM_END); + return (sql_command_flags[command] & CF_WRITE_LOGS_COMMAND) != 0; +} + void execute_init_command(THD *thd, sys_var_str *init_command_var, rw_lock_t *var_mutex) { @@ -493,7 +542,7 @@ int mysql_table_dump(THD *thd, LEX_STRING *db, char *tbl_name) if (lower_case_table_names) my_casedn_str(files_charset_info, tbl_name); - if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT))) + if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT, 0))) DBUG_RETURN(1); if (check_one_table_access(thd, SELECT_ACL, table_list)) @@ -553,7 +602,7 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion) thd->server_status&= ~SERVER_STATUS_IN_TRANS; res= ha_commit(thd); thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_KEEP_LOG); - thd->no_trans_update.all= FALSE; + thd->transaction.all.modified_non_trans_table= FALSE; break; case COMMIT_RELEASE: do_release= 1; /* fall through */ @@ -571,7 +620,7 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion) if (ha_rollback(thd)) res= -1; thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_KEEP_LOG); - thd->no_trans_update.all= FALSE; + thd->transaction.all.modified_non_trans_table= FALSE; if (!res && (completion == ROLLBACK_AND_CHAIN)) res= begin_trans(thd); break; @@ -954,14 +1003,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd, /* Locked closure of all tables */ TABLE_LIST table_list; LEX_STRING conv_name; - size_t dummy; /* used as fields initializator */ lex_start(thd); status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS]); bzero((char*) &table_list,sizeof(table_list)); - if (thd->copy_db_to(&table_list.db, &dummy)) + if (thd->copy_db_to(&table_list.db, &table_list.db_length)) break; /* We have name + wildcard in packet, separated by endzero @@ -1025,8 +1073,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, HA_CREATE_INFO create_info; status_var_increment(thd->status_var.com_stat[SQLCOM_CREATE_DB]); - if (thd->LEX_STRING_make(&db, packet, packet_length -1) || - thd->LEX_STRING_make(&alias, db.str, db.length) || + if (thd->make_lex_string(&db, packet, packet_length - 1, FALSE) || + thd->make_lex_string(&alias, db.str, db.length, FALSE) || check_db_name(&db)) { my_error(ER_WRONG_DB_NAME, MYF(0), db.str ? db.str : "NULL"); @@ -1046,7 +1094,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, status_var_increment(thd->status_var.com_stat[SQLCOM_DROP_DB]); LEX_STRING db; - if (thd->LEX_STRING_make(&db, packet, packet_length - 1) || + if (thd->make_lex_string(&db, packet, packet_length - 1, FALSE) || check_db_name(&db)) { my_error(ER_WRONG_DB_NAME, MYF(0), db.str ? db.str : "NULL"); @@ -1297,7 +1345,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, void log_slow_statement(THD *thd) { - time_t start_of_query; DBUG_ENTER("log_slow_statement"); /* @@ -1308,25 +1355,25 @@ void log_slow_statement(THD *thd) if (unlikely(thd->in_sub_stmt)) DBUG_VOID_RETURN; // Don't set time for sub stmt - start_of_query= thd->start_time; - thd->end_time(); // Set start time - /* Do not log administrative statements unless the appropriate option is set; do not log into slow log if reading from backup. */ if (thd->enable_slow_log && !thd->user_time) { - thd->proc_info="logging slow query"; + ulonglong end_utime_of_query= thd->current_utime(); - if ((ulong) (thd->start_time - thd->time_after_lock) > - thd->variables.long_query_time || - ((thd->server_status & - (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) && - (specialflag & SPECIAL_LOG_QUERIES_NOT_USING_INDEXES))) + thd->proc_info="logging slow query"; + if (((end_utime_of_query - thd->utime_after_lock) > + thd->variables.long_query_time || + ((thd->server_status & + (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) && + opt_log_queries_not_using_indexes && + !(sql_command_flags[thd->lex->sql_command] & CF_STATUS_COMMAND))) && + thd->examined_row_count >= thd->variables.min_examined_row_limit) { thd->status_var.long_query_count++; - slow_log_print(thd, thd->query, thd->query_length, start_of_query); + slow_log_print(thd, thd->query, thd->query_length, end_utime_of_query); } } DBUG_VOID_RETURN; @@ -1391,7 +1438,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, LEX_STRING db; size_t dummy; if (lex->select_lex.db == NULL && - thd->copy_db_to(&lex->select_lex.db, &dummy)) + lex->copy_db_to(&lex->select_lex.db, &dummy)) { DBUG_RETURN(1); } @@ -1684,8 +1731,7 @@ mysql_execute_command(THD *thd) variables, but for now this is probably good enough. Don't reset warnings when executing a stored routine. */ - if ((all_tables || &lex->select_lex != lex->all_selects_list || - lex->sroutines.records) && !thd->spcont) + if ((all_tables || !lex->is_single_level_stmt()) && !thd->spcont) mysql_reset_errors(thd, 0); #ifdef HAVE_REPLICATION @@ -1780,6 +1826,8 @@ mysql_execute_command(THD *thd) #endif status_var_increment(thd->status_var.com_stat[lex->sql_command]); + DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE); + switch (lex->sql_command) { case SQLCOM_SHOW_EVENTS: if ((res= check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0, @@ -2449,7 +2497,7 @@ end_with_restore_list: check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, 1, 0))) goto error; } - query_cache_invalidate3(thd, first_table, 0); + if (end_active_trans(thd) || mysql_rename_tables(thd, first_table, 0)) goto error; break; @@ -3210,6 +3258,8 @@ end_with_restore_list: } case SQLCOM_SHOW_CREATE_DB: { + DBUG_EXECUTE_IF("4x_server_emul", + my_error(ER_UNKNOWN_ERROR, MYF(0)); goto error;); if (check_db_name(&lex->name)) { my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str); @@ -3331,6 +3381,8 @@ end_with_restore_list: } case SQLCOM_REVOKE_ALL: { + if (end_active_trans(thd)) + goto error; if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) && check_global_access(thd,CREATE_USER_ACL)) break; @@ -3342,6 +3394,9 @@ end_with_restore_list: case SQLCOM_REVOKE: case SQLCOM_GRANT: { + if (end_active_trans(thd)) + goto error; + if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL, first_table ? first_table->db : select_lex->db, first_table ? &first_table->grant.privilege : 0, @@ -3594,7 +3649,8 @@ end_with_restore_list: res= TRUE; // cannot happen else { - if (((thd->options & OPTION_KEEP_LOG) || thd->no_trans_update.all) && + if (((thd->options & OPTION_KEEP_LOG) || + thd->transaction.all.modified_non_trans_table) && !thd->slave_thread) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARNING_NOT_COMPLETE_ROLLBACK, @@ -4174,8 +4230,8 @@ create_sp_error: thd->transaction.xid_state.xa_state=XA_ACTIVE; thd->transaction.xid_state.xid.set(thd->lex->xid); xid_cache_insert(&thd->transaction.xid_state); + thd->transaction.all.modified_non_trans_table= FALSE; thd->options= ((thd->options & ~(OPTION_KEEP_LOG)) | OPTION_BEGIN); - thd->no_trans_update.all= FALSE; thd->server_status|= SERVER_STATUS_IN_TRANS; send_ok(thd); break; @@ -4269,7 +4325,7 @@ create_sp_error: break; } thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); - thd->no_trans_update.all= FALSE; + thd->transaction.all.modified_non_trans_table= FALSE; thd->server_status&= ~SERVER_STATUS_IN_TRANS; xid_cache_delete(&thd->transaction.xid_state); thd->transaction.xid_state.xa_state=XA_NOTR; @@ -4300,7 +4356,7 @@ create_sp_error: else send_ok(thd); thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); - thd->no_trans_update.all= FALSE; + thd->transaction.all.modified_non_trans_table= FALSE; thd->server_status&= ~SERVER_STATUS_IN_TRANS; xid_cache_delete(&thd->transaction.xid_state); thd->transaction.xid_state.xa_state=XA_NOTR; @@ -5042,7 +5098,11 @@ bool check_merge_table_access(THD *thd, char *db, #ifndef EMBEDDED_LIBRARY -#define used_stack(A,B) (long)(A > B ? A - B : B - A) +#if STACK_DIRECTION < 0 +#define used_stack(A,B) (long) (A - B) +#else +#define used_stack(A,B) (long) (B - A) +#endif #ifndef DBUG_OFF long max_stack_used; @@ -5147,10 +5207,10 @@ void mysql_reset_thd_for_next_command(THD *thd) if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) { thd->options&= ~OPTION_KEEP_LOG; - thd->no_trans_update.all= FALSE; + thd->transaction.all.modified_non_trans_table= FALSE; } DBUG_ASSERT(thd->security_ctx== &thd->main_security_ctx); - thd->tmp_table_used= 0; + thd->thread_specific_used= FALSE; if (!thd->in_sub_stmt) { if (opt_bin_log) @@ -5201,6 +5261,11 @@ mysql_new_select(LEX *lex, bool move_down) select_lex->init_query(); select_lex->init_select(); lex->nest_level++; + if (lex->nest_level > (int) MAX_SELECT_NESTING) + { + my_error(ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT,MYF(0),MAX_SELECT_NESTING); + DBUG_RETURN(1); + } select_lex->nest_level= lex->nest_level; /* Don't evaluate this subquery during statement prepare even if @@ -5320,11 +5385,12 @@ void mysql_init_multi_delete(LEX *lex) /** Parse a query. - @param thd Current thread - @param inBuf Begining of the query text - @param length Length of the query text - @param [out] semicolon For multi queries, position of the character of - the next query in the query text. + + @param thd Current thread + @param inBuf Begining of the query text + @param length Length of the query text + @param[out] found_semicolon For multi queries, position of the character of + the next query in the query text. */ void mysql_parse(THD *thd, const char *inBuf, uint length, @@ -5392,8 +5458,9 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, (thd->query_length= (ulong)(*found_semicolon - thd->query))) thd->query_length--; /* Actually execute the query */ - mysql_execute_command(thd); - query_cache_end_of_result(thd); + lex->set_trg_event_type_for_tables(); + mysql_execute_command(thd); + query_cache_end_of_result(thd); } } } @@ -5635,7 +5702,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, LEX_STRING *alias, ulong table_options, thr_lock_type lock_type, - List<index_hint> *index_hints_arg, + List<Index_hint> *index_hints_arg, LEX_STRING *option) { register TABLE_LIST *ptr; @@ -5680,7 +5747,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->db= table->db.str; ptr->db_length= table->db.length; } - else if (thd->copy_db_to(&ptr->db, &ptr->db_length)) + else if (lex->copy_db_to(&ptr->db, &ptr->db_length)) DBUG_RETURN(0); ptr->alias= alias_str; @@ -7190,3 +7257,7 @@ bool parse_sql(THD *thd, return err_status; } + +/** + @} (end of group Runtime_Environment) +*/ diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 423f39a66dc..8a8a03cb4e4 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -36,7 +36,7 @@ #include "mysql_priv.h" #include <errno.h> #include <m_ctype.h> -#include "md5.h" +#include "my_md5.h" #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" @@ -2743,7 +2743,8 @@ uint32 get_list_array_idx_for_endpoint(partition_info *part_info, uint min_list_index= 0, max_list_index= part_info->no_list_values - 1; longlong list_value; /* Get the partitioning function value for the endpoint */ - longlong part_func_value= part_val_int(part_info->part_expr); + longlong part_func_value= + part_info->part_expr->val_int_endpoint(left_endpoint, &include_endpoint); bool unsigned_flag= part_info->part_expr->unsigned_flag; DBUG_ENTER("get_list_array_idx_for_endpoint"); @@ -2887,7 +2888,9 @@ uint32 get_partition_id_range_for_endpoint(partition_info *part_info, uint max_partition= part_info->no_parts - 1; uint min_part_id= 0, max_part_id= max_partition, loc_part_id; /* Get the partitioning function value for the endpoint */ - longlong part_func_value= part_val_int(part_info->part_expr); + longlong part_func_value= + part_info->part_expr->val_int_endpoint(left_endpoint, &include_endpoint); + bool unsigned_flag= part_info->part_expr->unsigned_flag; DBUG_ENTER("get_partition_id_range_for_endpoint"); @@ -2900,6 +2903,8 @@ uint32 get_partition_id_range_for_endpoint(partition_info *part_info, } if (unsigned_flag) part_func_value-= 0x8000000000000000ULL; + if (left_endpoint && !include_endpoint) + part_func_value++; while (max_part_id > min_part_id) { loc_part_id= (max_part_id + min_part_id + 1) >> 1; @@ -3325,7 +3330,9 @@ static uint32 get_sub_part_id_from_key(const TABLE *table,uchar *buf, key_restore(buf, (uchar*)key_spec->key, key_info, key_spec->length); if (likely(rec0 == buf)) + { part_id= part_info->get_subpartition_id(part_info); + } else { Field **part_field_array= part_info->subpart_field_array; @@ -3368,8 +3375,10 @@ bool get_part_id_from_key(const TABLE *table, uchar *buf, KEY *key_info, key_restore(buf, (uchar*)key_spec->key, key_info, key_spec->length); if (likely(rec0 == buf)) + { result= part_info->get_part_partition_id(part_info, part_id, &func_value); + } else { Field **part_field_array= part_info->part_field_array; @@ -3415,8 +3424,10 @@ void get_full_part_id_from_key(const TABLE *table, uchar *buf, key_restore(buf, (uchar*)key_spec->key, key_info, key_spec->length); if (likely(rec0 == buf)) + { result= part_info->get_partition_id(part_info, &part_spec->start_part, &func_value); + } else { Field **part_field_array= part_info->full_part_field_array; @@ -6582,8 +6593,6 @@ void make_used_partitions_str(partition_info *part_info, String *parts_str) #ifdef WITH_PARTITION_STORAGE_ENGINE static void set_up_range_analysis_info(partition_info *part_info) { - enum_monotonicity_info minfo; - /* Set the catch-all default */ part_info->get_part_iter_for_interval= NULL; part_info->get_subpart_iter_for_interval= NULL; @@ -6595,11 +6604,8 @@ static void set_up_range_analysis_info(partition_info *part_info) switch (part_info->part_type) { case RANGE_PARTITION: case LIST_PARTITION: - minfo= part_info->part_expr->get_monotonicity_info(); - if (minfo != NON_MONOTONIC) + if (part_info->part_expr->get_monotonicity_info() != NON_MONOTONIC) { - part_info->range_analysis_include_bounds= - test(minfo == MONOTONIC_INCREASING); part_info->get_part_iter_for_interval= get_part_iter_for_interval_via_mapping; goto setup_subparts; @@ -6767,8 +6773,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info, index-in-ordered-array-of-list-constants (for LIST) space. */ store_key_image_to_rec(field, min_value, field_len); - bool include_endp= part_info->range_analysis_include_bounds || - !test(flags & NEAR_MIN); + bool include_endp= !test(flags & NEAR_MIN); part_iter->part_nums.start= get_endpoint(part_info, 1, include_endp); part_iter->part_nums.cur= part_iter->part_nums.start; if (part_iter->part_nums.start == max_endpoint_val) @@ -6782,8 +6787,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info, else { store_key_image_to_rec(field, max_value, field_len); - bool include_endp= part_info->range_analysis_include_bounds || - !test(flags & NEAR_MAX); + bool include_endp= !test(flags & NEAR_MAX); part_iter->part_nums.end= get_endpoint(part_info, 0, include_endp); if (part_iter->part_nums.start == part_iter->part_nums.end && !part_iter->ret_null_part) diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index a44e20b8daf..1ec56e0caea 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -181,6 +181,7 @@ public: TYPELIB* plugin_var_typelib(void); uchar* value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); bool check(THD *thd, set_var *var); + bool check_default(enum_var_type type) { return is_readonly(); } void set_default(THD *thd, enum_var_type type); bool update(THD *thd, set_var *var); }; @@ -1058,8 +1059,12 @@ err: } -static uchar *get_hash_key(const uchar *buff, size_t *length, - my_bool not_used __attribute__((unused))) +extern "C" uchar *get_plugin_hash_key(const uchar *, size_t *, my_bool); +extern "C" uchar *get_bookmark_hash_key(const uchar *, size_t *, my_bool); + + +uchar *get_plugin_hash_key(const uchar *buff, size_t *length, + my_bool not_used __attribute__((unused))) { struct st_plugin_int *plugin= (st_plugin_int *)buff; *length= (uint)plugin->name.length; @@ -1067,8 +1072,8 @@ static uchar *get_hash_key(const uchar *buff, size_t *length, } -static uchar *get_bookmark_hash_key(const uchar *buff, size_t *length, - my_bool not_used __attribute__((unused))) +uchar *get_bookmark_hash_key(const uchar *buff, size_t *length, + my_bool not_used __attribute__((unused))) { struct st_bookmark *var= (st_bookmark *)buff; *length= var->name_len + 1; @@ -1115,7 +1120,7 @@ int plugin_init(int *argc, char **argv, int flags) for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++) { if (hash_init(&plugin_hash[i], system_charset_info, 16, 0, 0, - get_hash_key, NULL, HASH_UNIQUE)) + get_plugin_hash_key, NULL, HASH_UNIQUE)) goto err; } @@ -1617,7 +1622,7 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl DBUG_RETURN(TRUE); /* need to open before acquiring LOCK_plugin or it will deadlock */ - if (! (table = open_ltable(thd, &tables, TL_WRITE))) + if (! (table = open_ltable(thd, &tables, TL_WRITE, 0))) DBUG_RETURN(TRUE); pthread_mutex_lock(&LOCK_plugin); @@ -1674,7 +1679,7 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name) tables.table_name= tables.alias= (char *)"plugin"; /* need to open before acquiring LOCK_plugin or it will deadlock */ - if (! (table= open_ltable(thd, &tables, TL_WRITE))) + if (! (table= open_ltable(thd, &tables, TL_WRITE, 0))) DBUG_RETURN(TRUE); pthread_mutex_lock(&LOCK_plugin); @@ -1702,9 +1707,10 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name) table->use_all_columns(); table->field[0]->store(name->str, name->length, system_charset_info); - if (! table->file->index_read_idx(table->record[0], 0, - (uchar *)table->field[0]->ptr, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (! table->file->index_read_idx_map(table->record[0], 0, + (uchar *)table->field[0]->ptr, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { int error; if ((error= table->file->ha_delete_row(table->record[0]))) @@ -2163,9 +2169,11 @@ static st_bookmark *register_var(const char *plugin, const char *name, size= sizeof(int); break; case PLUGIN_VAR_LONG: + case PLUGIN_VAR_ENUM: size= sizeof(long); break; case PLUGIN_VAR_LONGLONG: + case PLUGIN_VAR_SET: size= sizeof(ulonglong); break; case PLUGIN_VAR_STR: @@ -2444,7 +2452,7 @@ void plugin_thdvar_cleanup(THD *thd) /** @brief Free values of thread variables of a plugin. - @detail This must be called before a plugin is deleted. Otherwise its + This must be called before a plugin is deleted. Otherwise its variables are no longer accessible and the value space is lost. Note that only string values with PLUGIN_VAR_MEMALLOC are allocated and must be freed. @@ -2606,6 +2614,7 @@ void sys_var_pluginvar::set_default(THD *thd, enum_var_type type) if (is_readonly()) return; + pthread_mutex_lock(&LOCK_global_system_variables); tgt= real_value_ptr(thd, type); src= ((void **) (plugin_var + 1) + 1); @@ -2622,12 +2631,14 @@ void sys_var_pluginvar::set_default(THD *thd, enum_var_type type) if (!(plugin_var->flags & PLUGIN_VAR_THDLOCAL) || type == OPT_GLOBAL) { - pthread_mutex_lock(&LOCK_plugin); plugin_var->update(thd, plugin_var, tgt, src); - pthread_mutex_unlock(&LOCK_plugin); + pthread_mutex_unlock(&LOCK_global_system_variables); } else + { + pthread_mutex_unlock(&LOCK_global_system_variables); plugin_var->update(thd, plugin_var, tgt, src); + } } @@ -2769,8 +2780,10 @@ static void plugin_opt_set_limits(struct my_option *options, options->arg_type= OPT_ARG; } +extern "C" my_bool get_one_plugin_option(int optid, const struct my_option *, + char *); -static my_bool get_one_option(int optid __attribute__((unused)), +my_bool get_one_plugin_option(int optid __attribute__((unused)), const struct my_option *opt, char *argument) { @@ -3078,7 +3091,7 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp, DBUG_RETURN(-1); } - error= handle_options(argc, &argv, opts, get_one_option); + error= handle_options(argc, &argv, opts, get_one_plugin_option); (*argc)++; /* add back one for the program name */ if (error) @@ -3140,7 +3153,7 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp, DBUG_RETURN(0); } - if (enabled_saved) + if (enabled_saved && global_system_variables.log_warnings) sql_print_information("Plugin '%s' disabled by command line option", tmp->name.str); DBUG_RETURN(1); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 406e242cada..9337a2aa329 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -112,7 +112,6 @@ public: /****************************************************************************/ /** - @class Prepared_statement @brief Prepared_statement: a statement that can contain placeholders */ @@ -1941,13 +1940,6 @@ void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length) /* Statement map deletes statement on erase */ thd->stmt_map.erase(stmt); } - else - { - 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; } @@ -2330,12 +2322,6 @@ void mysql_stmt_execute(THD *thd, char *packet_arg, uint packet_length) test(flags & (ulong) CURSOR_TYPE_READ_ONLY)); if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(), WAIT_PRIOR); - if (error == 0) - { - 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: @@ -2880,6 +2866,20 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) error= parse_sql(thd, &lip, NULL) || thd->net.report_error || init_param_array(this); + lex->set_trg_event_type_for_tables(); + + /* Remember the current database. */ + + if (thd->db && thd->db_length) + { + db= this->strmake(thd->db, thd->db_length); + db_length= thd->db_length; + } + else + { + db= NULL; + db_length= 0; + } /* While doing context analysis of the query (in check_prepared_statement) @@ -2929,6 +2929,29 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) init_stmt_after_parse(lex); state= Query_arena::PREPARED; flags&= ~ (uint) IS_IN_USE; + + /* + Log COM_EXECUTE to the general log. Note, that in case of SQL + prepared statements this causes two records to be output: + + Query PREPARE stmt from @user_variable + Prepare <statement SQL text> + + This is considered user-friendly, since in the + second log entry we output the actual statement text. + + Do not print anything if this is an SQL prepared statement and + we're inside a stored procedure (also called Dynamic SQL) -- + sub-statements inside stored procedures are not logged into + the general log. + */ + if (thd->spcont == NULL) + { + const char *format= "[%lu] %.*b"; + general_log_print(thd, COM_STMT_PREPARE, format, id, + query_length, query); + + } } DBUG_RETURN(error); } @@ -2964,6 +2987,13 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) Query_arena *old_stmt_arena; bool error= TRUE; + char saved_cur_db_name_buf[NAME_LEN+1]; + LEX_STRING saved_cur_db_name= + { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) }; + bool cur_db_changed; + + LEX_STRING stmt_db_name= { db, db_length }; + status_var_increment(thd->status_var.com_stmt_execute); /* Check if we got an error when sending long data */ @@ -3012,6 +3042,21 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) */ thd->set_n_backup_statement(this, &stmt_backup); + + /* + Change the current database (if needed). + + Force switching, because the database of the prepared statement may be + NULL (prepared statements can be created while no current database + selected). + */ + + if (mysql_opt_change_db(thd, &stmt_db_name, &saved_cur_db_name, TRUE, + &cur_db_changed)) + goto error; + + /* Allocate query. */ + if (expanded_query->length() && alloc_query(thd, (char*) expanded_query->ptr(), expanded_query->length()+1)) @@ -3040,6 +3085,8 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) thd->protocol= protocol; /* activate stmt protocol */ + /* Go! */ + if (open_cursor) error= mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR, &result, &cursor); @@ -3058,6 +3105,17 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) } } + /* + Restore the current database (if changed). + + Force switching back to the saved current database (if changed), + because it may be NULL. In this case, mysql_change_db() would generate + an error. + */ + + if (cur_db_changed) + mysql_change_db(thd, &saved_cur_db_name, TRUE); + thd->protocol= &thd->protocol_text; /* use normal protocol */ /* Assert that if an error, no cursor is open */ @@ -3075,6 +3133,28 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) if (state == Query_arena::PREPARED) state= Query_arena::EXECUTED; + /* + Log COM_EXECUTE to the general log. Note, that in case of SQL + prepared statements this causes two records to be output: + + Query EXECUTE <statement name> + Execute <statement SQL text> + + This is considered user-friendly, since in the + second log entry we output values of parameter markers. + + Do not print anything if this is an SQL prepared statement and + we're inside a stored procedure (also called Dynamic SQL) -- + sub-statements inside stored procedures are not logged into + the general log. + */ + if (error == 0 && thd->spcont == NULL) + { + const char *format= "[%lu] %.*b"; + general_log_print(thd, COM_STMT_EXECUTE, format, id, + thd->query_length, thd->query); + } + error: flags&= ~ (uint) IS_IN_USE; return error; diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 866d82377c0..750bcd50479 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -37,7 +37,6 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) TABLE_LIST *ren_table= 0; int to_table; char *rename_log_table[2]= {NULL, NULL}; - int disable_logs= 0; DBUG_ENTER("mysql_rename_tables"); /* @@ -80,12 +79,6 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) 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; @@ -136,18 +129,15 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) 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)) + pthread_mutex_lock(&LOCK_open); + if (lock_table_names_exclusively(thd, table_list)) + { + pthread_mutex_unlock(&LOCK_open); goto err; - + } + error=0; if ((ren_table=rename_tables(thd,table_list,0))) { @@ -170,6 +160,17 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) error= 1; } + /* + An exclusive lock on table names is satisfactory to ensure + no other thread accesses this table. + However, NDB assumes that handler::rename_tables is called under + LOCK_open. And it indeed is, from ALTER TABLE. + TODO: remove this limitation. + We still should unlock LOCK_open as early as possible, to provide + higher concurrency - query_cache_invalidate can take minutes to + complete. + */ + pthread_mutex_unlock(&LOCK_open); /* Lets hope this doesn't fail as the result will be messy */ if (!silent && !error) @@ -178,17 +179,14 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) send_ok(thd); } + if (!error) + query_cache_invalidate3(thd, table_list, 0); + + pthread_mutex_lock(&LOCK_open); unlock_table_names(thd, table_list, (TABLE_LIST*) 0); + pthread_mutex_unlock(&LOCK_open); 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); } diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 1616e895107..a6e52c05219 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -762,7 +762,7 @@ err: DBUG_VOID_RETURN; } -int start_slave(THD* thd , MASTER_INFO* mi, bool net_report) +int start_slave(THD* thd , Master_info* mi, bool net_report) { int slave_errno= 0; int thread_mask; @@ -799,7 +799,7 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report) if (thd->lex->mi.pos) { - mi->rli.until_condition= RELAY_LOG_INFO::UNTIL_MASTER_POS; + mi->rli.until_condition= Relay_log_info::UNTIL_MASTER_POS; mi->rli.until_log_pos= thd->lex->mi.pos; /* We don't check thd->lex->mi.log_file_name for NULL here @@ -810,7 +810,7 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report) } else if (thd->lex->mi.relay_log_pos) { - mi->rli.until_condition= RELAY_LOG_INFO::UNTIL_RELAY_POS; + mi->rli.until_condition= Relay_log_info::UNTIL_RELAY_POS; mi->rli.until_log_pos= thd->lex->mi.relay_log_pos; strmake(mi->rli.until_log_name, thd->lex->mi.relay_log_name, sizeof(mi->rli.until_log_name)-1); @@ -818,7 +818,7 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report) else mi->rli.clear_until_condition(); - if (mi->rli.until_condition != RELAY_LOG_INFO::UNTIL_NONE) + if (mi->rli.until_condition != Relay_log_info::UNTIL_NONE) { /* Preparing members for effective until condition checking */ const char *p= fn_ext(mi->rli.until_log_name); @@ -840,7 +840,7 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report) /* mark the cached result of the UNTIL comparison as "undefined" */ mi->rli.until_log_names_cmp_result= - RELAY_LOG_INFO::UNTIL_LOG_NAMES_CMP_UNKNOWN; + Relay_log_info::UNTIL_LOG_NAMES_CMP_UNKNOWN; /* Issuing warning then started without --skip-slave-start */ if (!opt_skip_slave_start) @@ -887,7 +887,7 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report) } -int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report ) +int stop_slave(THD* thd, Master_info* mi, bool net_report ) { DBUG_ENTER("stop_slave"); @@ -953,7 +953,7 @@ int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report ) */ -int reset_slave(THD *thd, MASTER_INFO* mi) +int reset_slave(THD *thd, Master_info* mi) { MY_STAT stat_area; char fname[FN_REFLEN]; @@ -1067,7 +1067,7 @@ void kill_zombie_dump_threads(uint32 slave_server_id) } -bool change_master(THD* thd, MASTER_INFO* mi) +bool change_master(THD* thd, Master_info* mi) { int thread_mask; const char* errmsg= 0; @@ -1442,13 +1442,11 @@ err: } if (errmsg) - { my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), "SHOW BINLOG EVENTS", errmsg); - DBUG_RETURN(TRUE); - } + else + send_eof(thd); - send_eof(thd); pthread_mutex_lock(&LOCK_thread_count); thd->current_linfo = 0; pthread_mutex_unlock(&LOCK_thread_count); diff --git a/sql/sql_repl.h b/sql/sql_repl.h index da50d47c60d..cf5201f17b1 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -42,20 +42,20 @@ extern my_bool opt_sporadic_binlog_dump_fail; pthread_mutex_unlock(&(thd)->LOCK_delete); \ } while(0) -int start_slave(THD* thd, MASTER_INFO* mi, bool net_report); -int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report); -bool change_master(THD* thd, MASTER_INFO* mi); +int start_slave(THD* thd, Master_info* mi, bool net_report); +int stop_slave(THD* thd, Master_info* mi, bool net_report); +bool change_master(THD* thd, Master_info* mi); bool mysql_show_binlog_events(THD* thd); int cmp_master_pos(const char* log_file_name1, ulonglong log_pos1, const char* log_file_name2, ulonglong log_pos2); -int reset_slave(THD *thd, MASTER_INFO* mi); +int reset_slave(THD *thd, Master_info* mi); int reset_master(THD* thd); bool purge_master_logs(THD* thd, const char* to_log); bool purge_master_logs_before_date(THD* thd, time_t purge_time); bool log_in_use(const char* log_name); void adjust_linfo_offsets(my_off_t purge_offset); bool show_binlogs(THD* thd); -extern int init_master_info(MASTER_INFO* mi); +extern int init_master_info(Master_info* mi); void kill_zombie_dump_threads(uint32 slave_server_id); int check_binlog_magic(IO_CACHE* log, const char** errmsg); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a855e536fec..1f206e2ec17 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -13,6 +13,10 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/** + @defgroup Query_Optimizer Query Optimizer + @{ +*/ /* mysql_select and join optimization */ @@ -192,6 +196,7 @@ static bool setup_new_fields(THD *thd, List<Item> &fields, List<Item> &all_fields, ORDER *new_order); static ORDER *create_distinct_group(THD *thd, Item **ref_pointer_array, ORDER *order, List<Item> &fields, + List<Item> &all_fields, bool *all_order_by_fields_used); static bool test_if_subpart(ORDER *a,ORDER *b); static TABLE *get_sort_by_table(ORDER *a,ORDER *b,TABLE_LIST *tables); @@ -540,6 +545,28 @@ JOIN::prepare(Item ***rref_pointer_array, fix_inner_refs(thd, all_fields, select_lex, ref_pointer_array)) DBUG_RETURN(-1); + if (group_list) + { + /* + Because HEAP tables can't index BIT fields we need to use an + additional hidden field for grouping because later it will be + converted to a LONG field. Original field will remain of the + BIT type and will be returned to a client. + */ + for (ORDER *ord= group_list; ord; ord= ord->next) + { + if ((*ord->item)->type() == Item::FIELD_ITEM && + (*ord->item)->field_type() == MYSQL_TYPE_BIT) + { + Item_field *field= new Item_field(thd, *(Item_field**)ord->item); + int el= all_fields.elements; + ref_pointer_array[el]= field; + all_fields.push_front(field); + ord->item= ref_pointer_array + el; + } + } + } + if (setup_ftfuncs(select_lex)) /* should be after having->fix_fields */ DBUG_RETURN(-1); @@ -1043,6 +1070,20 @@ JOIN::optimize() find_field_in_order_list, (void *) group_list)) { + /* + We have found that grouping can be removed since groups correspond to + only one row anyway, but we still have to guarantee correct result + order. The line below effectively rewrites the query from GROUP BY + <fields> to ORDER BY <fields>. One exception is if skip_sort_order is + set (see above), then we can simply skip GROUP BY. + */ + order= skip_sort_order ? 0 : group_list; + /* + If we have an IGNORE INDEX FOR GROUP BY(fields) clause, this must be + rewritten to IGNORE INDEX FOR ORDER BY(fields). + */ + join_tab->table->keys_in_use_for_order_by= + join_tab->table->keys_in_use_for_group_by; group_list= 0; group= 0; } @@ -1082,12 +1123,13 @@ JOIN::optimize() skip_sort_order= test_if_skip_sort_order(tab, order, select_limit, 1, &tab->table->keys_in_use_for_order_by); if ((group_list=create_distinct_group(thd, select_lex->ref_pointer_array, - order, fields_list, + order, fields_list, all_fields, &all_order_fields_used))) { bool skip_group= (skip_sort_order && test_if_skip_sort_order(tab, group_list, select_limit, 1, &tab->table->keys_in_use_for_group_by) != 0); + count_field_types(select_lex, &tmp_table_param, all_fields, 0); if ((skip_group && all_order_fields_used) || select_limit == HA_POS_ERROR || (order && !skip_sort_order)) @@ -1135,6 +1177,7 @@ JOIN::optimize() order=0; // The output has only one row simple_order=1; select_distinct= 0; // No need in distinct for 1 row + group_optimized_away= 1; } calc_group_buffer(this, group_list); @@ -1625,6 +1668,10 @@ JOIN::exec() DBUG_VOID_RETURN; } + if ((this->select_lex->options & OPTION_SCHEMA_TABLE) && + get_schema_tables_result(this, PROCESSED_BY_JOIN_EXEC)) + DBUG_VOID_RETURN; + if (select_options & SELECT_DESCRIBE) { /* @@ -1670,13 +1717,6 @@ JOIN::exec() */ curr_join->examined_rows= 0; - if ((curr_join->select_lex->options & OPTION_SCHEMA_TABLE) && - !thd->lex->describe && - get_schema_tables_result(curr_join, PROCESSED_BY_JOIN_EXEC)) - { - DBUG_VOID_RETURN; - } - /* Create a tmp table if distinct or if the sort is too complicated */ if (need_tmp) { @@ -2455,7 +2495,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds, no_partitions_used) && !s->dependent && (table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT) && - !table->fulltext_searched) + !table->fulltext_searched && !join->no_const_tables) { set_position(join,const_count++,s,(KEYUSE*) 0); } @@ -3565,10 +3605,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, uint and_level,i,found_eq_constant; KEY_FIELD *key_fields, *end, *field; uint sz; - uint m= 1; - - if (cond_equal && cond_equal->max_members) - m= cond_equal->max_members; + uint m= max(select_lex->max_equal_elems,1); /* We use the same piece of memory to store both KEY_FIELD @@ -3588,7 +3625,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, it is considered as sargable only for its first argument. Multiple equality can add elements that are filled after substitution of field arguments by equal fields. There - can be not more than cond_equal->max_members such substitutions. + can be not more than select_lex->max_equal_elems such + substitutions. */ sz= max(sizeof(KEY_FIELD),sizeof(SARGABLE_PARAM))* (((thd->lex->current_select->cond_count+1)*2 + @@ -4468,8 +4506,12 @@ choose_plan(JOIN *join, table_map join_tables) /* Store the cost of this query into a user variable + Don't update last_query_cost for statements that are not "flat joins" : + i.e. they have subqueries, unions or call stored procedures. + TODO: calculate a correct cost for a query with subqueries and UNIONs. */ - join->thd->status_var.last_query_cost= join->best_read; + if (join->thd->lex->is_single_level_stmt()) + join->thd->status_var.last_query_cost= join->best_read; DBUG_RETURN(FALSE); } @@ -6085,7 +6127,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) /* Fix for EXPLAIN */ if (sel->quick) - join->best_positions[i].records_read= sel->quick->records; + join->best_positions[i].records_read= (double)sel->quick->records; } else { @@ -6451,6 +6493,7 @@ void JOIN_TAB::cleanup() quick= 0; x_free(cache.buff); cache.buff= 0; + limit= 0; if (table) { if (table->key_read) @@ -7390,8 +7433,7 @@ static bool check_equality(THD *thd, Item *item, COND_EQUAL *cond_equal, just an argument of a comparison predicate. The function also determines the maximum number of members in equality lists of each Item_cond_and object assigning it to - cond_equal->max_members of this object and updating accordingly - the upper levels COND_EQUAL structures. + thd->lex->current_select->max_equal_elems. NOTES Multiple equality predicate =(f1,..fn) is equivalent to the conjuction of @@ -7436,7 +7478,6 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond, COND_EQUAL *inherited) { Item_equal *item_equal; - uint members; COND_EQUAL cond_equal; cond_equal.upper_levels= inherited; @@ -7474,19 +7515,8 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond, { item_equal->fix_length_and_dec(); item_equal->update_used_tables(); - members= item_equal->members(); - if (cond_equal.max_members < members) - cond_equal.max_members= members; - } - members= cond_equal.max_members; - if (inherited && inherited->max_members < members) - { - do - { - inherited->max_members= members; - inherited= inherited->upper_levels; - } - while (inherited); + set_if_bigger(thd->lex->current_select->max_equal_elems, + item_equal->members()); } ((Item_cond_and*)cond)->cond_equal= cond_equal; @@ -7541,10 +7571,12 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond, { item_equal->fix_length_and_dec(); item_equal->update_used_tables(); - return item_equal; } else - return eq_list.pop(); + item_equal= (Item_equal *) eq_list.pop(); + set_if_bigger(thd->lex->current_select->max_equal_elems, + item_equal->members()); + return item_equal; } else { @@ -7560,9 +7592,8 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond, { item_equal->fix_length_and_dec(); item_equal->update_used_tables(); - members= item_equal->members(); - if (cond_equal.max_members < members) - cond_equal.max_members= members; + set_if_bigger(thd->lex->current_select->max_equal_elems, + item_equal->members()); } and_cond->cond_equal= cond_equal; args->concat((List<Item> *)&cond_equal.current_level); @@ -8704,9 +8735,15 @@ static void restore_prev_nj_state(JOIN_TAB *last) { TABLE_LIST *last_emb= last->table->pos_in_table_list->embedding; JOIN *join= last->join; - while (last_emb && !(--last_emb->nested_join->counter)) + while (last_emb) { - join->cur_embedding_map &= last_emb->nested_join->nj_map; + if (!(--last_emb->nested_join->counter)) + join->cur_embedding_map&= ~last_emb->nested_join->nj_map; + else if (last_emb->nested_join->join_list.elements-1 == + last_emb->nested_join->counter) + join->cur_embedding_map|= last_emb->nested_join->nj_map; + else + break; last_emb= last_emb->embedding; } } @@ -9414,7 +9451,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, bool using_unique_constraint= 0; bool use_packed_rows= 0; bool not_all_columns= !(select_options & TMP_TABLE_ALL_COLUMNS); - char *tmpname, *tmppath, path[FN_REFLEN], table_name[NAME_LEN+1]; + char *tmpname,path[FN_REFLEN]; uchar *pos, *group_buff, *bitmaps; uchar *null_flags; Field **reg_field, **from_field, **default_field; @@ -9438,12 +9475,12 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, temp_pool_slot = bitmap_lock_set_next(&temp_pool); if (temp_pool_slot != MY_BIT_NONE) // we got a slot - sprintf(table_name, "%s_%lx_%i", tmp_file_prefix, + sprintf(path, "%s_%lx_%i", tmp_file_prefix, current_pid, temp_pool_slot); else { /* if we run out of slots or we are not using tempool */ - sprintf(table_name, "%s%lx_%lx_%x", tmp_file_prefix,current_pid, + sprintf(path,"%s%lx_%lx_%x", tmp_file_prefix,current_pid, thd->thread_id, thd->tmp_table++); } @@ -9451,8 +9488,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, No need to change table name to lower case as we are only creating MyISAM or HEAP tables here */ - fn_format(path, table_name, mysql_tmpdir, "", - MY_REPLACE_EXT|MY_UNPACK_FILENAME); + fn_format(path, path, mysql_tmpdir, "", MY_REPLACE_EXT|MY_UNPACK_FILENAME); + if (group) { @@ -9498,8 +9535,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, sizeof(*key_part_info)*(param->group_parts+1), ¶m->start_recinfo, sizeof(*param->recinfo)*(field_count*2+4), - &tmppath, (uint) strlen(path)+1, - &tmpname, (uint) strlen(table_name)+1, + &tmpname, (uint) strlen(path)+1, &group_buff, (group && ! using_unique_constraint ? param->group_length : 0), &bitmaps, bitmap_buffer_size(field_count)*2, @@ -9518,8 +9554,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, DBUG_RETURN(NULL); /* purecov: inspected */ } param->items_to_copy= copy_func; - strmov(tmppath, path); - strmov(tmpname, table_name); + strmov(tmpname,path); /* make table according to fields */ bzero((char*) table,sizeof(*table)); @@ -9544,7 +9579,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, table->keys_in_use_for_query.init(); table->s= share; - init_tmp_table_share(share, "", 0, tmpname, tmppath); + init_tmp_table_share(share, "", 0, tmpname, tmpname); share->blob_field= blob_field; share->blob_ptr_size= portable_sizeof_char_ptr; share->db_low_byte_first=1; // True for HEAP and MyISAM @@ -10193,7 +10228,7 @@ static bool open_tmp_table(TABLE *table) { int error; if ((error=table->file->ha_open(table, table->s->table_name.str,O_RDWR, - HA_OPEN_TMP_TABLE))) + HA_OPEN_TMP_TABLE | HA_OPEN_INTERNAL_TABLE))) { table->file->print_error(error,MYF(0)); /* purecov: inspected */ table->db_stat=0; @@ -10431,8 +10466,7 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param, /* remove heap table and change to use myisam table */ (void) table->file->ha_rnd_end(); - (void) table->file->close(); - (void) table->file->delete_table(table->s->table_name.str); + (void) table->file->close(); // This deletes the table ! delete table->file; table->file=0; plugin_unlock(0, table->s->db_plugin); @@ -11149,10 +11183,10 @@ int safe_index_read(JOIN_TAB *tab) { int error; TABLE *table= tab->table; - if ((error=table->file->index_read(table->record[0], - tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts), - HA_READ_KEY_EXACT))) + if ((error=table->file->index_read_map(table->record[0], + tab->ref.key_buff, + make_prev_keypart_map(tab->ref.key_parts), + HA_READ_KEY_EXACT))) return report_error(table, error); return 0; } @@ -11288,10 +11322,10 @@ join_read_const(JOIN_TAB *tab) error=HA_ERR_KEY_NOT_FOUND; else { - error=table->file->index_read_idx(table->record[0],tab->ref.key, - (uchar*) tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts), - HA_READ_KEY_EXACT); + error=table->file->index_read_idx_map(table->record[0],tab->ref.key, + (uchar*) tab->ref.key_buff, + make_prev_keypart_map(tab->ref.key_parts), + HA_READ_KEY_EXACT); } if (error) { @@ -11332,10 +11366,10 @@ join_read_key(JOIN_TAB *tab) table->status=STATUS_NOT_FOUND; return -1; } - error=table->file->index_read(table->record[0], - tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts), - HA_READ_KEY_EXACT); + error=table->file->index_read_map(table->record[0], + tab->ref.key_buff, + make_prev_keypart_map(tab->ref.key_parts), + HA_READ_KEY_EXACT); if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) return report_error(table, error); } @@ -11361,10 +11395,10 @@ join_read_always_key(JOIN_TAB *tab) } if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref)) return -1; - if ((error=table->file->index_read(table->record[0], - tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts), - HA_READ_KEY_EXACT))) + if ((error=table->file->index_read_map(table->record[0], + tab->ref.key_buff, + make_prev_keypart_map(tab->ref.key_parts), + HA_READ_KEY_EXACT))) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) return report_error(table, error); @@ -11389,8 +11423,9 @@ join_read_last_key(JOIN_TAB *tab) table->file->ha_index_init(tab->ref.key, tab->sorted); if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref)) return -1; - if ((error=table->file->index_read_last(table->record[0], - tab->ref.key_buff, make_prev_keypart_map(tab->ref.key_parts)))) + if ((error=table->file->index_read_last_map(table->record[0], + tab->ref.key_buff, + make_prev_keypart_map(tab->ref.key_parts)))) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) return report_error(table, error); @@ -11737,7 +11772,8 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), if (!join->first_record || end_of_records || (idx=test_if_group_changed(join->group_fields)) >= 0) { - if (join->first_record || (end_of_records && !join->group)) + if (join->first_record || + (end_of_records && !join->group && !join->group_optimized_away)) { if (join->procedure) join->procedure->end_group(); @@ -11930,9 +11966,10 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), if (item->maybe_null) group->buff[-1]= (char) group->field->is_null(); } - if (!table->file->index_read(table->record[1], - join->tmp_table_param.group_buff, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (!table->file->index_read_map(table->record[1], + join->tmp_table_param.group_buff, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { /* Update old record */ restore_record(table,record[1]); update_tmptable_sum_func(join->sum_funcs,table); @@ -12284,6 +12321,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx, key_part_end=key_part+table->key_info[idx].key_parts; key_part_map const_key_parts=table->const_key_parts[idx]; int reverse=0; + my_bool on_primary_key= FALSE; DBUG_ENTER("test_if_order_by_key"); for (; order ; order=order->next, const_key_parts>>=1) @@ -12298,7 +12336,36 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx, for (; const_key_parts & 1 ; const_key_parts>>= 1) key_part++; - if (key_part == key_part_end || key_part->field != field) + if (key_part == key_part_end) + { + /* + We are at the end of the key. Check if the engine has the primary + key as a suffix to the secondary keys. If it has continue to check + the primary key as a suffix. + */ + if (!on_primary_key && + (table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) && + table->s->primary_key != MAX_KEY) + { + on_primary_key= TRUE; + key_part= table->key_info[table->s->primary_key].key_part; + key_part_end=key_part+table->key_info[table->s->primary_key].key_parts; + const_key_parts=table->const_key_parts[table->s->primary_key]; + + for (; const_key_parts & 1 ; const_key_parts>>= 1) + key_part++; + /* + The primary and secondary key parts were all const (i.e. there's + one row). The sorting doesn't matter. + */ + if (key_part == key_part_end && reverse == 0) + DBUG_RETURN(1); + } + else + DBUG_RETURN(0); + } + + if (key_part->field != field) DBUG_RETURN(0); /* set flag to 1 if we can use read-next on key, else to -1 */ @@ -12309,7 +12376,8 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx, reverse=flag; // Remember if reverse key_part++; } - *used_key_parts= (uint) (key_part - table->key_info[idx].key_part); + *used_key_parts= on_primary_key ? table->key_info[idx].key_parts : + (uint) (key_part - table->key_info[idx].key_part); if (reverse == -1 && !(table->file->index_flags(idx, *used_key_parts-1, 1) & HA_READ_PREV)) reverse= 0; // Index can't be used @@ -12558,9 +12626,12 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, { int ref_key; uint ref_key_parts; + int order_direction; + uint used_key_parts; TABLE *table=tab->table; SQL_SELECT *select=tab->select; key_map usable_keys; + QUICK_SELECT_I *save_quick= 0; DBUG_ENTER("test_if_skip_sort_order"); LINT_INIT(ref_key_parts); @@ -12595,6 +12666,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, else if (select && select->quick) // Range found by opt_range { int quick_type= select->quick->get_type(); + save_quick= select->quick; /* assume results are not ordered when index merge is used TODO: sergeyp: Results of all index merge selects actually are ordered @@ -12614,8 +12686,6 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, /* We come here when there is a REF key. */ - int order_direction; - uint used_key_parts; if (!usable_keys.is_set(ref_key)) { /* @@ -12676,63 +12746,33 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, } /* Check if we get the rows in requested sorted order by using the key */ if (usable_keys.is_set(ref_key) && - (order_direction = test_if_order_by_key(order,table,ref_key, - &used_key_parts))) - { - if (order_direction == -1) // If ORDER BY ... DESC - { - if (select && select->quick) - { - /* - Don't reverse the sort order, if it's already done. - (In some cases test_if_order_by_key() can be called multiple times - */ - if (!select->quick->reverse_sorted()) - { - QUICK_SELECT_DESC *tmp; - int quick_type= select->quick->get_type(); - if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE || - quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT || - quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION || - quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX) - DBUG_RETURN(0); // Use filesort - - /* ORDER BY range_key DESC */ - tmp= new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick), - used_key_parts); - if (!tmp || tmp->error) - { - delete tmp; - DBUG_RETURN(0); // Reverse sort not supported - } - select->quick=tmp; - } - DBUG_RETURN(1); - } - if (tab->ref.key_parts < used_key_parts) - { - /* - SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC - - Use a traversal function that starts by reading the last row - with key part (A) and then traverse the index backwards. - */ - tab->read_first_record= join_read_last_key; - tab->read_record.read_record= join_read_prev_same; - /* fall through */ - } - } - else if (select && select->quick) - select->quick->sorted= 1; - DBUG_RETURN(1); /* No need to sort */ - } + (order_direction= test_if_order_by_key(order,table,ref_key, + &used_key_parts))) + goto check_reverse_order; } - else { - /* check if we can use a key to resolve the group */ - /* Tables using JT_NEXT are handled here */ + /* + Check whether there is an index compatible with the given order + usage of which is cheaper than usage of the ref_key index (ref_key>=0) + or a table scan. + It may be the case if ORDER/GROUP BY is used with LIMIT. + */ uint nr; key_map keys; + uint best_key_parts; + int best_key_direction; + ha_rows best_records; + double read_time; + int best_key= -1; + bool is_best_covering= FALSE; + double fanout= 1; + JOIN *join= tab->join; + uint tablenr= tab - join->join_tab; + ha_rows table_records= table->file->stats.records; + bool group= join->group && order == join->group_list; + LINT_INIT(best_key_parts); + LINT_INIT(best_key_direction); + LINT_INIT(best_records); /* filesort() and join cache are usually faster than reading in @@ -12745,7 +12785,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, resolved with a key; This is because filesort() is usually faster than retrieving all rows through an index. */ - if (select_limit >= table->file->stats.records) + if (select_limit >= table_records) { keys= *table->file->keys_to_use_for_scanning(); keys.merge(table->covering_keys); @@ -12756,38 +12796,227 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, This is to allow users to use index in ORDER BY. */ if (table->force_index) - keys.merge(table->keys_in_use_for_query); + keys.merge(group ? table->keys_in_use_for_group_by : + table->keys_in_use_for_order_by); keys.intersect(usable_keys); } else keys= usable_keys; + read_time= join->best_positions[tablenr].read_time; + for (uint i= tablenr+1; i < join->tables; i++) + fanout*= join->best_positions[i].records_read; // fanout is always >= 1 + for (nr=0; nr < table->s->keys ; nr++) { - uint not_used; - if (keys.is_set(nr)) + int direction; + if (keys.is_set(nr) && + (direction= test_if_order_by_key(order, table, nr, &used_key_parts))) + { + bool is_covering= table->covering_keys.is_set(nr) || + nr == table->s->primary_key && + table->file->primary_key_is_clustered(); + + /* + Don't use an index scan with ORDER BY without limit. + For GROUP BY without limit always use index scan + if there is a suitable index. + Why we hold to this asymmetry hardly can be explained + rationally. It's easy to demonstrate that using + temporary table + filesort could be cheaper for grouping + queries too. + */ + if (is_covering || + select_limit != HA_POS_ERROR || + ref_key < 0 && (group || table->force_index)) + { + double rec_per_key; + double index_scan_time; + KEY *keyinfo= tab->table->key_info+nr; + if (select_limit == HA_POS_ERROR) + select_limit= table_records; + if (group) + { + rec_per_key= keyinfo->rec_per_key[used_key_parts-1]; + set_if_bigger(rec_per_key, 1); + /* + With a grouping query each group containing on average + rec_per_key records produces only one row that will + be included into the result set. + */ + if (select_limit > table_records/rec_per_key) + select_limit= table_records; + else + select_limit= (ha_rows) (select_limit*rec_per_key); + } + /* + If tab=tk is not the last joined table tn then to get first + L records from the result set we can expect to retrieve + only L/fanout(tk,tn) where fanout(tk,tn) says how many + rows in the record set on average will match each row tk. + Usually our estimates for fanouts are too pessimistic. + So the estimate for L/fanout(tk,tn) will be too optimistic + and as result we'll choose an index scan when using ref/range + access + filesort will be cheaper. + */ + select_limit= (ha_rows) (select_limit < fanout ? + 1 : select_limit/fanout); + /* + We assume that each of the tested indexes is not correlated + with ref_key. Thus, to select first N records we have to scan + N/selectivity(ref_key) index entries. + selectivity(ref_key) = #scanned_records/#table_records = + table->quick_condition_rows/table_records. + In any case we can't select more than #table_records. + N/(table->quick_condition_rows/table_records) > table_records + <=> N > table->quick_condition_rows. + */ + if (select_limit > table->quick_condition_rows) + select_limit= table_records; + else + select_limit= (ha_rows) (select_limit * + (double) table_records / + table->quick_condition_rows); + rec_per_key= keyinfo->rec_per_key[keyinfo->key_parts-1]; + set_if_bigger(rec_per_key, 1); + /* + Here we take into account the fact that rows are + accessed in sequences rec_per_key records in each. + Rows in such a sequence are supposed to be ordered + by rowid/primary key. When reading the data + in a sequence we'll touch not more pages than the + table file contains. + TODO. Use the formula for a disk sweep sequential access + to calculate the cost of accessing data rows for one + index entry. + */ + index_scan_time= select_limit/rec_per_key * + min(rec_per_key, table->file->scan_time()); + if (is_covering || + ref_key < 0 && (group || table->force_index) || + index_scan_time < read_time) + { + ha_rows quick_records= table_records; + if (is_best_covering && !is_covering) + continue; + if (table->quick_keys.is_set(nr)) + quick_records= table->quick_rows[nr]; + if (best_key < 0 || + (select_limit <= min(quick_records,best_records) ? + keyinfo->key_parts < best_key_parts : + quick_records < best_records)) + { + best_key= nr; + best_key_parts= keyinfo->key_parts; + best_records= quick_records; + is_best_covering= is_covering; + best_key_direction= direction; + } + } + } + } + } + if (best_key >= 0) + { + bool quick_created= FALSE; + if (table->quick_keys.is_set(best_key) && best_key != ref_key) { - int flag; - if ((flag= test_if_order_by_key(order, table, nr, ¬_used))) + key_map map; + map.clear_all(); // Force the creation of quick select + map.set_bit(best_key); // only best_key. + quick_created= + select->test_quick_select(join->thd, map, 0, + join->select_options & OPTION_FOUND_ROWS ? + HA_POS_ERROR : + join->unit->select_limit_cnt, + 0) > 0; + } + if (!no_changes) + { + if (!quick_created) { - if (!no_changes) - { - tab->index=nr; - tab->read_first_record= (flag > 0 ? join_read_first: - join_read_last); - tab->type=JT_NEXT; // Read with index_first(), index_next() - if (table->covering_keys.is_set(nr)) - { - table->key_read=1; - table->file->extra(HA_EXTRA_KEYREAD); - } - } - DBUG_RETURN(1); + tab->index= best_key; + tab->read_first_record= best_key_direction > 0 ? + join_read_first:join_read_last; + tab->type=JT_NEXT; // Read with index_first(), index_next() + if (table->covering_keys.is_set(best_key)) + { + table->key_read=1; + table->file->extra(HA_EXTRA_KEYREAD); + } + table->file->ha_index_or_rnd_end(); + if (join->select_options & SELECT_DESCRIBE) + { + tab->ref.key= -1; + tab->ref.key_parts= 0; + if (select && select->quick) + { + delete select->quick; + select->quick= 0; + } + if (select_limit < table_records) + tab->limit= select_limit; + } + } + } + used_key_parts= best_key_parts; + order_direction= best_key_direction; + } + else + DBUG_RETURN(0); + } + +check_reverse_order: + if (order_direction == -1) // If ORDER BY ... DESC + { + if (select && select->quick) + { + /* + Don't reverse the sort order, if it's already done. + (In some cases test_if_order_by_key() can be called multiple times + */ + if (!select->quick->reverse_sorted()) + { + QUICK_SELECT_DESC *tmp; + int quick_type= select->quick->get_type(); + if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE || + quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT || + quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION || + quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX) + { + tab->limit= 0; + select->quick= save_quick; + DBUG_RETURN(0); // Use filesort + } + + /* ORDER BY range_key DESC */ + tmp= new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick), + used_key_parts); + if (!tmp || tmp->error) + { + delete tmp; + select->quick= save_quick; + tab->limit= 0; + DBUG_RETURN(0); // Reverse sort not supported } + select->quick=tmp; } } + else if (tab->ref.key >= 0 && tab->ref.key_parts <= used_key_parts) + { + /* + SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC + + Use a traversal function that starts by reading the last row + with key part (A) and then traverse the index backwards. + */ + tab->read_first_record= join_read_last_key; + tab->read_record.read_record= join_read_prev_same; + } } - DBUG_RETURN(0); // Can't use index. + else if (select && select->quick) + select->quick->sorted= 1; + DBUG_RETURN(1); } @@ -12897,7 +13126,6 @@ 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) && - !thd->lex->describe && get_schema_tables_result(join, PROCESSED_BY_CREATE_SORT_INDEX)) goto err; @@ -13025,7 +13253,7 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having) field_count++; } - if (!field_count && !(join->select_options & OPTION_FOUND_ROWS)) + if (!field_count && !(join->select_options & OPTION_FOUND_ROWS) && !having) { // only const items with no OPTION_FOUND_ROWS join->unit->select_limit_cnt= 1; // Only send first row DBUG_RETURN(0); @@ -13302,7 +13530,8 @@ static int join_init_cache(THD *thd,JOIN_TAB *tables,uint table_count) { reg1 uint i; - uint length,blobs,size; + uint length, blobs; + size_t size; CACHE_FIELD *copy,**blob_ptr; JOIN_CACHE *cache; JOIN_TAB *join_tab; @@ -13418,7 +13647,7 @@ store_record_in_cache(JOIN_CACHE *cache) length=cache->length; if (cache->blobs) length+=used_blob_length(cache->blob_ptr); - if ((last_record=(length+cache->length > (uint) (cache->end - pos)))) + if ((last_record= (length + cache->length > (size_t) (cache->end - pos)))) cache->ptr_record=cache->records; /* @@ -13464,7 +13693,7 @@ store_record_in_cache(JOIN_CACHE *cache) } } cache->pos=pos; - return last_record || (uint) (cache->end -pos) < cache->length; + return last_record || (size_t) (cache->end - pos) < cache->length; } @@ -13912,11 +14141,12 @@ setup_new_fields(THD *thd, List<Item> &fields, static ORDER * create_distinct_group(THD *thd, Item **ref_pointer_array, - ORDER *order_list, List<Item> &fields, + ORDER *order_list, List<Item> &fields, + List<Item> &all_fields, bool *all_order_by_fields_used) { List_iterator<Item> li(fields); - Item *item; + Item *item, **orig_ref_pointer_array= ref_pointer_array; ORDER *order,*group,**prev; *all_order_by_fields_used= 1; @@ -13956,12 +14186,31 @@ create_distinct_group(THD *thd, Item **ref_pointer_array, ORDER *ord=(ORDER*) thd->calloc(sizeof(ORDER)); if (!ord) return 0; - /* - We have here only field_list (not all_field_list), so we can use - simple indexing of ref_pointer_array (order in the array and in the - list are same) - */ - ord->item= ref_pointer_array; + + if (item->type() == Item::FIELD_ITEM && + item->field_type() == MYSQL_TYPE_BIT) + { + /* + Because HEAP tables can't index BIT fields we need to use an + additional hidden field for grouping because later it will be + converted to a LONG field. Original field will remain of the + BIT type and will be returned to a client. + */ + Item_field *new_item= new Item_field(thd, (Item_field*)item); + int el= all_fields.elements; + orig_ref_pointer_array[el]= new_item; + all_fields.push_front(new_item); + ord->item= orig_ref_pointer_array + el; + } + else + { + /* + We have here only field_list (not all_field_list), so we can use + simple indexing of ref_pointer_array (order in the array and in the + list are same) + */ + ord->item= ref_pointer_array; + } ord->asc=1; *prev=ord; prev= &ord->next; @@ -15349,6 +15598,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, { JOIN_TAB *tab=join->join_tab+i; TABLE *table=tab->table; + TABLE_LIST *table_list= tab->table->pos_in_table_list; char buff[512]; char buff1[512], buff2[512], buff3[512]; char keylen_str_buf[64]; @@ -15484,37 +15734,64 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, } else { - item_list.push_back(item_null); + if (table_list->schema_table && + table_list->schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE) + { + const char *tmp_buff; + int f_idx; + if (table_list->has_db_lookup_value) + { + f_idx= table_list->schema_table->idx_field1; + tmp_buff= table_list->schema_table->fields_info[f_idx].field_name; + tmp2.append(tmp_buff, strlen(tmp_buff), cs); + } + if (table_list->has_table_lookup_value) + { + if (table_list->has_db_lookup_value) + tmp2.append(','); + f_idx= table_list->schema_table->idx_field2; + tmp_buff= table_list->schema_table->fields_info[f_idx].field_name; + tmp2.append(tmp_buff, strlen(tmp_buff), cs); + } + if (tmp2.length()) + item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs)); + else + item_list.push_back(item_null); + } + else + item_list.push_back(item_null); item_list.push_back(item_null); item_list.push_back(item_null); } /* Add "rows" field to item_list. */ - ha_rows examined_rows; - if (tab->select && tab->select->quick) - examined_rows= tab->select->quick->records; - else if (tab->type == JT_NEXT || tab->type == JT_ALL) - examined_rows= tab->table->file->records(); + if (table_list->schema_table) + { + item_list.push_back(item_null); + } else - examined_rows=(ha_rows)join->best_positions[i].records_read; - - item_list.push_back(new Item_int((longlong) (ulonglong) examined_rows, - MY_INT64_NUM_DECIMAL_DIGITS)); - - /* Add "filtered" field to item_list. */ - if (join->thd->lex->describe & DESCRIBE_EXTENDED) { - Item_float *filtered; - float f; - if (examined_rows) - f= (float) (100.0 * join->best_positions[i].records_read / - examined_rows); + ha_rows examined_rows; + if (tab->select && tab->select->quick) + examined_rows= tab->select->quick->records; + else if (tab->type == JT_NEXT || tab->type == JT_ALL) + examined_rows= tab->limit ? tab->limit : tab->table->file->records(); else - f= 0.0; - item_list.push_back((filtered= new Item_float(f))); - filtered->decimals= 2; - } + examined_rows=(ha_rows)join->best_positions[i].records_read; + + item_list.push_back(new Item_int((longlong) (ulonglong) examined_rows, + MY_INT64_NUM_DECIMAL_DIGITS)); + /* Add "filtered" field to item_list. */ + if (join->thd->lex->describe & DESCRIBE_EXTENDED) + { + float f= 0.0; + if (examined_rows) + f= (float) (100.0 * join->best_positions[i].records_read / + examined_rows); + item_list.push_back(new Item_float(f, 2)); + } + } /* Build "Extra" field and add it to item_list. */ my_bool key_read=table->key_read; @@ -15582,6 +15859,24 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, extra.append(STRING_WITH_LEN("; Using where")); } } + if (table_list->schema_table && + table_list->schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE) + { + if (!table_list->table_open_method) + extra.append(STRING_WITH_LEN("; Skip_open_table")); + else if (table_list->table_open_method == OPEN_FRM_ONLY) + extra.append(STRING_WITH_LEN("; Open_frm_only")); + else + extra.append(STRING_WITH_LEN("; Open_full_table")); + if (table_list->has_db_lookup_value && + table_list->has_table_lookup_value) + extra.append(STRING_WITH_LEN("; Scanned 0 databases")); + else if (table_list->has_db_lookup_value || + table_list->has_table_lookup_value) + extra.append(STRING_WITH_LEN("; Scanned 1 database")); + else + extra.append(STRING_WITH_LEN("; Scanned all databases")); + } if (key_read) { if (quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX) @@ -15753,11 +16048,11 @@ static void print_join(THD *thd, String *str, List<TABLE_LIST> *tables) Print table as it should be in join list SYNOPSIS - st_table_list::print(); + TABLE_LIST::print(); str string where table should bbe printed */ -void st_table_list::print(THD *thd, String *str) +void TABLE_LIST::print(THD *thd, String *str) { if (nested_join) { @@ -15963,3 +16258,7 @@ bool JOIN::change_result(select_result *res) } DBUG_RETURN(FALSE); } + +/** + @} (end of group Query_Optimizer) +*/ diff --git a/sql/sql_select.h b/sql/sql_select.h index 98f2a7829dd..efa92432e2b 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -194,6 +194,12 @@ typedef struct st_join_table { enum join_type type; bool cached_eq_ref_table,eq_ref_table,not_used_in_distinct; bool sorted; + /* + If it's not 0 the number stored this field indicates that the index + scan has been chosen to access the table data and we expect to scan + this number of rows for the table. + */ + ha_rows limit; TABLE_REF ref; JOIN_CACHE cache; JOIN *join; @@ -316,11 +322,27 @@ public: SELECT_LEX_UNIT *unit; // select that processed SELECT_LEX *select_lex; + /* + TRUE <=> optimizer must not mark any table as a constant table. + This is needed for subqueries in form "a IN (SELECT .. UNION SELECT ..): + when we optimize the select that reads the results of the union from a + temporary table, we must not mark the temp. table as constant because + the number of rows in it may vary from one subquery execution to another. + */ + bool no_const_tables; JOIN *tmp_join; // copy of this JOIN to be used with temporary tables ROLLUP rollup; // Used with rollup bool select_distinct; // Set if SELECT DISTINCT + /* + If we have the GROUP BY statement in the query, + but the group_list was emptied by optimizer, this + flag is TRUE. + It happens when fields in the GROUP BY are from + constant table + */ + bool group_optimized_away; /* simple_xxxxx is set if ORDER/GROUP BY doesn't include any references @@ -429,6 +451,7 @@ public: zero_result_cause= 0; optimized= 0; cond_equal= 0; + group_optimized_away= 0; all_fields= fields_arg; fields_list= fields_arg; @@ -436,6 +459,8 @@ public: tmp_table_param.init(); tmp_table_param.end_write_records= HA_POS_ERROR; rollup.state= ROLLUP::STATE_NONE; + + no_const_tables= FALSE; } int prepare(Item ***rref_pointer_array, TABLE_LIST *tables, uint wind_num, diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index ac5ea6f4ac4..a780c561ffe 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -366,7 +366,7 @@ insert_server(THD *thd, FOREIGN_SERVER *server) 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))) + if (! (table= open_ltable(thd, &tables, TL_WRITE, 0))) goto end; /* insert the server into the table */ @@ -516,9 +516,10 @@ int insert_server_record(TABLE *table, FOREIGN_SERVER *server) system_charset_info); /* read index until record is that specified in server_name */ - if ((error= table->file->index_read_idx(table->record[0], 0, - (uchar *)table->field[0]->ptr, HA_WHOLE_KEY, - HA_READ_KEY_EXACT))) + if ((error= table->file->index_read_idx_map(table->record[0], 0, + (uchar *)table->field[0]->ptr, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT))) { /* if not found, err */ if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) @@ -588,7 +589,7 @@ int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options) if ((error= delete_server_record_in_cache(server_options))) goto end; - if (! (table= open_ltable(thd, &tables, TL_WRITE))) + if (! (table= open_ltable(thd, &tables, TL_WRITE, 0))) { error= my_errno; goto end; @@ -705,7 +706,7 @@ int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered) tables.db= (char*)"mysql"; tables.alias= tables.table_name= (char*)"servers"; - if (!(table= open_ltable(thd, &tables, TL_WRITE))) + if (!(table= open_ltable(thd, &tables, TL_WRITE, 0))) { error= my_errno; goto end; @@ -858,9 +859,10 @@ update_server_record(TABLE *table, FOREIGN_SERVER *server) server->server_name_length, system_charset_info); - if ((error= table->file->index_read_idx(table->record[0], 0, - (uchar *)table->field[0]->ptr, ~(longlong)0, - HA_READ_KEY_EXACT))) + if ((error= table->file->index_read_idx_map(table->record[0], 0, + (uchar *)table->field[0]->ptr, + ~(longlong)0, + HA_READ_KEY_EXACT))) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) table->file->print_error(error, MYF(0)); @@ -914,9 +916,10 @@ delete_server_record(TABLE *table, /* 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, - (uchar *)table->field[0]->ptr, HA_WHOLE_KEY, - HA_READ_KEY_EXACT))) + if ((error= table->file->index_read_idx_map(table->record[0], 0, + (uchar *)table->field[0]->ptr, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT))) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) table->file->print_error(error, MYF(0)); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 1c1b86d11a7..38db2fe85a5 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -446,13 +446,15 @@ bool mysqld_show_column_types(THD *thd) find_files_result -find_files(THD *thd, List<char> *files, const char *db, +find_files(THD *thd, List<LEX_STRING> *files, const char *db, const char *path, const char *wild, bool dir) { uint i; char *ext; MY_DIR *dirp; FILEINFO *file; + LEX_STRING *file_name= 0; + uint file_name_len; #ifndef NO_EMBEDDED_ACCESS_CHECKS uint col_access=thd->col_access; #endif @@ -501,10 +503,16 @@ find_files(THD *thd, List<char> *files, const char *db, #endif if (!MY_S_ISDIR(file->mystat->st_mode)) continue; - VOID(filename_to_tablename(file->name, uname, sizeof(uname))); + + file_name_len= filename_to_tablename(file->name, uname, sizeof(uname)); if (wild && wild_compare(uname, wild, 0)) continue; - file->name= uname; + if (!(file_name= + thd->make_lex_string(file_name, uname, file_name_len, TRUE))) + { + my_dirend(dirp); + DBUG_RETURN(FIND_FILES_OOM); + } } else { @@ -513,16 +521,15 @@ find_files(THD *thd, List<char> *files, const char *db, is_prefix(file->name, tmp_file_prefix)) continue; *ext=0; - VOID(filename_to_tablename(file->name, uname, sizeof(uname))); - file->name= uname; + file_name_len= filename_to_tablename(file->name, uname, sizeof(uname)); if (wild) { if (lower_case_table_names) { - if (wild_case_compare(files_charset_info, file->name, wild)) + if (wild_case_compare(files_charset_info, uname, wild)) continue; } - else if (wild_compare(file->name,wild,0)) + else if (wild_compare(uname, wild, 0)) continue; } } @@ -532,14 +539,16 @@ find_files(THD *thd, List<char> *files, const char *db, { table_list.db= (char*) db; table_list.db_length= strlen(db); - table_list.table_name= file->name; - table_list.table_name_length= strlen(file->name); + table_list.table_name= uname; + table_list.table_name_length= file_name_len; table_list.grant.privilege=col_access; if (check_grant(thd, TABLE_ACLS, &table_list, 1, 1, 1)) continue; } #endif - if (files->push_back(thd->strdup(file->name))) + if (!(file_name= + thd->make_lex_string(file_name, uname, file_name_len, TRUE)) || + files->push_back(file_name)) { my_dirend(dirp); DBUG_RETURN(FIND_FILES_OOM); @@ -548,7 +557,7 @@ find_files(THD *thd, List<char> *files, const char *db, DBUG_PRINT("info",("found: %d files", files->elements)); my_dirend(dirp); - VOID(ha_find_files(thd,db,path,wild,dir,files)); + VOID(ha_find_files(thd, db, path, wild, dir, files)); DBUG_RETURN(FIND_FILES_OK); } @@ -642,7 +651,8 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) if (table_list->view) { - protocol->store(buffer.ptr(), buffer.length(), &my_charset_bin); + protocol->store(buffer.ptr(), buffer.length(), + table_list->view_creation_ctx->get_client_cs()); protocol->store(table_list->view_creation_ctx->get_client_cs()->csname, system_charset_info); @@ -968,6 +978,60 @@ static void append_directory(THD *thd, String *packet, const char *dir_type, #define LIST_PROCESS_HOST_LEN 64 +static bool get_field_default_value(THD *thd, TABLE *table, + Field *field, String *def_value, + bool quoted) +{ + bool has_default; + bool has_now_default; + + /* + We are using CURRENT_TIMESTAMP instead of NOW because it is + more standard + */ + has_now_default= table->timestamp_field == field && + field->unireg_check != Field::TIMESTAMP_UN_FIELD; + + has_default= (field->type() != FIELD_TYPE_BLOB && + !(field->flags & NO_DEFAULT_VALUE_FLAG) && + field->unireg_check != Field::NEXT_NUMBER && + !((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) + && has_now_default)); + + def_value->length(0); + if (has_default) + { + if (has_now_default) + def_value->append(STRING_WITH_LEN("CURRENT_TIMESTAMP")); + else if (!field->is_null()) + { // Not null by default + char tmp[MAX_FIELD_WIDTH]; + String type(tmp, sizeof(tmp), field->charset()); + field->val_str(&type); + if (type.length()) + { + String def_val; + uint dummy_errors; + /* convert to system_charset_info == utf8 */ + def_val.copy(type.ptr(), type.length(), field->charset(), + system_charset_info, &dummy_errors); + if (quoted) + append_unescaped(def_value, def_val.ptr(), def_val.length()); + else + def_value->append(def_val.ptr(), def_val.length()); + } + else if (quoted) + def_value->append(STRING_WITH_LEN("''")); + } + else if (field->maybe_null() && quoted) + def_value->append(STRING_WITH_LEN("NULL")); // Null as default + else + return 0; + + } + return has_default; +} + /* Build a CREATE TABLE statement for a table. @@ -982,11 +1046,11 @@ static void append_directory(THD *thd, String *packet, const char *dir_type, to tailor the format of the statement. Can be NULL, in which case only SQL_MODE is considered when building the statement. - + NOTE Currently always return 0, but might return error code in the future. - + RETURN 0 OK */ @@ -995,9 +1059,10 @@ 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]; + char tmp[MAX_FIELD_WIDTH], *for_str, buff[128], def_value_buf[MAX_FIELD_WIDTH]; const char *alias; String type(tmp, sizeof(tmp), system_charset_info); + String def_value(def_value_buf, sizeof(def_value_buf), system_charset_info); Field **ptr,*field; uint primary_key; KEY *key_info; @@ -1050,8 +1115,6 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, for (ptr=table->field ; (field= *ptr); ptr++) { - bool has_default; - bool has_now_default; uint flags = field->flags; if (ptr != table->field) @@ -1099,44 +1162,10 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN(" NULL")); } - /* - Again we are using CURRENT_TIMESTAMP instead of NOW because it is - more standard - */ - has_now_default= table->timestamp_field == field && - field->unireg_check != Field::TIMESTAMP_UN_FIELD; - - 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)) - && has_now_default)); - - if (has_default) + if (get_field_default_value(thd, table, field, &def_value, 1)) { packet->append(STRING_WITH_LEN(" DEFAULT ")); - if (has_now_default) - packet->append(STRING_WITH_LEN("CURRENT_TIMESTAMP")); - else if (!field->is_null()) - { // Not null by default - type.set(tmp, sizeof(tmp), field->charset()); - field->val_str(&type); - if (type.length()) - { - String def_val; - uint dummy_errors; - /* convert to system_charset_info == utf8 */ - def_val.copy(type.ptr(), type.length(), field->charset(), - system_charset_info, &dummy_errors); - append_unescaped(packet, def_val.ptr(), def_val.length()); - } - else - packet->append(STRING_WITH_LEN("''")); - } - else if (field->maybe_null()) - packet->append(STRING_WITH_LEN("NULL")); // Null as default - else - packet->append(tmp); + packet->append(def_value.ptr(), def_value.length(), system_charset_info); } if (!limited_mysql_mode && table->timestamp_field == field && @@ -1678,11 +1707,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) if (mysys_var) pthread_mutex_unlock(&mysys_var->mutex); -#ifdef EXTRA_DEBUG - thd_info->start_time= tmp->time_after_lock; -#else thd_info->start_time= tmp->start_time; -#endif thd_info->query=0; if (tmp->query) { @@ -1701,7 +1726,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) VOID(pthread_mutex_unlock(&LOCK_thread_count)); thread_info *thd_info; - time_t now= time(0); + time_t now= my_time(0); while ((thd_info=thread_infos.get())) { protocol->prepare_for_resend(); @@ -1731,7 +1756,7 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) TABLE *table= tables->table; CHARSET_INFO *cs= system_charset_info; char *user; - time_t now= time(0); + time_t now= my_time(0); DBUG_ENTER("fill_process_list"); user= thd->security_ctx->master_access & PROCESS_ACL ? @@ -2084,11 +2109,11 @@ static bool show_status_array(THD *thd, const char *wild, */ switch (show_type) { case SHOW_DOUBLE_STATUS: - { value= ((char *) status_var + (ulong) value); - end= buff + sprintf(buff, "%f", *(double*) value); + /* fall through */ + case SHOW_DOUBLE: + end= buff + my_sprintf(buff, (buff, "%f", *(double*) value)); break; - } case SHOW_LONG_STATUS: value= ((char *) status_var + (ulong) value); /* fall through */ @@ -2098,6 +2123,7 @@ static bool show_status_array(THD *thd, const char *wild, break; case SHOW_LONGLONG_STATUS: value= ((char *) status_var + (ulonglong) value); + /* fall through */ case SHOW_LONGLONG: end= longlong10_to_str(*(longlong*) value, buff, 10); break; @@ -2191,30 +2217,14 @@ void calc_sum_of_all_status(STATUS_VAR *to) } -LEX_STRING *make_lex_string(THD *thd, LEX_STRING *lex_str, - const char* str, uint length, - bool allocate_lex_string) -{ - MEM_ROOT *mem= thd->mem_root; - if (allocate_lex_string) - if (!(lex_str= (LEX_STRING *)thd->alloc(sizeof(LEX_STRING)))) - return 0; - lex_str->str= strmake_root(mem, str, length); - lex_str->length= length; - return lex_str; -} - - -/* INFORMATION_SCHEMA name */ -LEX_STRING INFORMATION_SCHEMA_NAME= { C_STRING_WITH_LEN("information_schema")}; - /* This is only used internally, but we need it here as a forward reference */ extern ST_SCHEMA_TABLE schema_tables[]; -typedef struct st_index_field_values +typedef struct st_lookup_field_values { - const char *db_value, *table_value; -} INDEX_FIELD_VALUES; + LEX_STRING db_value, table_value; + bool wild_db_value, wild_table_value; +} LOOKUP_FIELD_VALUES; /* @@ -2245,38 +2255,11 @@ bool schema_table_store_record(THD *thd, TABLE *table) } -void get_index_field_values(LEX *lex, INDEX_FIELD_VALUES *index_field_values) -{ - const char *wild= lex->wild ? lex->wild->ptr() : NullS; - switch (lex->sql_command) { - case SQLCOM_SHOW_DATABASES: - index_field_values->db_value= wild; - break; - case SQLCOM_SHOW_TABLES: - case SQLCOM_SHOW_TABLE_STATUS: - case SQLCOM_SHOW_TRIGGERS: - case SQLCOM_SHOW_EVENTS: - index_field_values->db_value= lex->select_lex.db; - index_field_values->table_value= wild; - break; - default: - index_field_values->db_value= NullS; - index_field_values->table_value= NullS; - break; - } -} - - int make_table_list(THD *thd, SELECT_LEX *sel, - char *db, char *table) + LEX_STRING *db_name, LEX_STRING *table_name) { Table_ident *table_ident; - LEX_STRING ident_db, ident_table; - ident_db.str= db; - ident_db.length= strlen(db); - ident_table.str= table; - ident_table.length= strlen(table); - table_ident= new Table_ident(thd, ident_db, ident_table, 1); + table_ident= new Table_ident(thd, *db_name, *table_name, 1); sel->init_query(); if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ)) return 1; @@ -2284,16 +2267,135 @@ int make_table_list(THD *thd, SELECT_LEX *sel, } +/** + @brief Get lookup value from the part of 'WHERE' condition + + @details This function gets lookup value from + the part of 'WHERE' condition if it's possible and + fill appropriate lookup_field_vals struct field + with this value. + + @param[in] thd thread handler + @param[in] item_func part of WHERE condition + @param[in] table I_S table + @param[in, out] lookup_field_vals Struct which holds lookup values + + @return void +*/ + +void get_lookup_value(THD *thd, Item_func *item_func, + TABLE_LIST *table, + LOOKUP_FIELD_VALUES *lookup_field_vals) +{ + ST_SCHEMA_TABLE *schema_table= table->schema_table; + ST_FIELD_INFO *field_info= schema_table->fields_info; + const char *field_name1= schema_table->idx_field1 >= 0 ? + field_info[schema_table->idx_field1].field_name : ""; + const char *field_name2= schema_table->idx_field2 >= 0 ? + field_info[schema_table->idx_field2].field_name : ""; + + if (item_func->functype() == Item_func::EQ_FUNC || + item_func->functype() == Item_func::EQUAL_FUNC) + { + int idx_field, idx_val; + char tmp[MAX_FIELD_WIDTH]; + String *tmp_str, str_buff(tmp, sizeof(tmp), system_charset_info); + Item_field *item_field; + CHARSET_INFO *cs= system_charset_info; + + if (item_func->arguments()[0]->type() == Item::FIELD_ITEM && + item_func->arguments()[1]->const_item()) + { + idx_field= 0; + idx_val= 1; + } + else if (item_func->arguments()[1]->type() == Item::FIELD_ITEM && + item_func->arguments()[0]->const_item()) + { + idx_field= 1; + idx_val= 0; + } + else + return; + + item_field= (Item_field*) item_func->arguments()[idx_field]; + if (table->table != item_field->field->table) + return; + tmp_str= item_func->arguments()[idx_val]->val_str(&str_buff); + + /* Lookup value is database name */ + if (!cs->coll->strnncollsp(cs, (uchar *) field_name1, strlen(field_name1), + (uchar *) item_field->field_name, + strlen(item_field->field_name), 0)) + { + thd->make_lex_string(&lookup_field_vals->db_value, tmp_str->ptr(), + tmp_str->length(), FALSE); + } + /* Lookup value is table name */ + else if (!cs->coll->strnncollsp(cs, (uchar *) field_name2, + strlen(field_name2), + (uchar *) item_field->field_name, + strlen(item_field->field_name), 0)) + { + thd->make_lex_string(&lookup_field_vals->table_value, tmp_str->ptr(), + tmp_str->length(), FALSE); + } + } + return; +} + + +/** + @brief Calculates lookup values from 'WHERE' condition + + @details This function calculates lookup value(database name, table name) + from 'WHERE' condition if it's possible and + fill lookup_field_vals struct fields with these values. + + @param[in] thd thread handler + @param[in] cond WHERE condition + @param[in] table I_S table + @param[in, out] lookup_field_vals Struct which holds lookup values + + @return void +*/ + +void calc_lookup_values_from_cond(THD *thd, COND *cond, TABLE_LIST *table, + LOOKUP_FIELD_VALUES *lookup_field_vals) +{ + if (!cond) + return; + + if (cond->type() == Item::COND_ITEM) + { + if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) + { + List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + Item *item; + while ((item= li++)) + { + if (item->type() == Item::FUNC_ITEM) + get_lookup_value(thd, (Item_func*)item, table, lookup_field_vals); + else + calc_lookup_values_from_cond(thd, item, table, lookup_field_vals); + } + } + return; + } + else if (cond->type() == Item::FUNC_ITEM) + get_lookup_value(thd, (Item_func*) cond, table, lookup_field_vals); + return; +} + + bool uses_only_table_name_fields(Item *item, TABLE_LIST *table) { if (item->type() == Item::FUNC_ITEM) { Item_func *item_func= (Item_func*)item; - Item **child; - Item **item_end= (item_func->arguments()) + item_func->argument_count(); - for (child= item_func->arguments(); child != item_end; child++) + for (uint i=0; i<item_func->argument_count(); i++) { - if (!uses_only_table_name_fields(*child, table)) + if (!uses_only_table_name_fields(item_func->arguments()[i], table)) return 0; } } @@ -2303,21 +2405,23 @@ bool uses_only_table_name_fields(Item *item, TABLE_LIST *table) CHARSET_INFO *cs= system_charset_info; ST_SCHEMA_TABLE *schema_table= table->schema_table; ST_FIELD_INFO *field_info= schema_table->fields_info; - const char *field_name1= schema_table->idx_field1 >= 0 ? field_info[schema_table->idx_field1].field_name : ""; - const char *field_name2= schema_table->idx_field2 >= 0 ? field_info[schema_table->idx_field2].field_name : ""; + const char *field_name1= schema_table->idx_field1 >= 0 ? + field_info[schema_table->idx_field1].field_name : ""; + const char *field_name2= schema_table->idx_field2 >= 0 ? + field_info[schema_table->idx_field2].field_name : ""; if (table->table != item_field->field->table || (cs->coll->strnncollsp(cs, (uchar *) field_name1, strlen(field_name1), - (uchar *) item_field->field_name, + (uchar *) item_field->field_name, strlen(item_field->field_name), 0) && cs->coll->strnncollsp(cs, (uchar *) field_name2, strlen(field_name2), - (uchar *) item_field->field_name, + (uchar *) item_field->field_name, strlen(item_field->field_name), 0))) return 0; } else if (item->type() == Item::REF_ITEM) return uses_only_table_name_fields(item->real_item(), table); - if (item->type() == Item::SUBSELECT_ITEM && - !item->const_item()) + + if (item->type() == Item::SUBSELECT_ITEM && !item->const_item()) return 0; return 1; @@ -2380,6 +2484,61 @@ static COND * make_cond_for_info_schema(COND *cond, TABLE_LIST *table) } +/** + @brief Calculate lookup values(database name, table name) + + @details This function calculates lookup values(database name, table name) + from 'WHERE' condition or wild values (for 'SHOW' commands only) + from LEX struct and fill lookup_field_vals struct field + with these values. + + @param[in] thd thread handler + @param[in] cond WHERE condition + @param[in] tables I_S table + @param[in, out] lookup_field_values Struct which holds lookup values + + @return void +*/ + +void get_lookup_field_values(THD *thd, COND *cond, TABLE_LIST *tables, + LOOKUP_FIELD_VALUES *lookup_field_values) +{ + LEX *lex= thd->lex; + const char *wild= lex->wild ? lex->wild->ptr() : NullS; + bzero((char*) lookup_field_values, sizeof(LOOKUP_FIELD_VALUES)); + switch (lex->sql_command) { + case SQLCOM_SHOW_DATABASES: + if (wild) + { + lookup_field_values->db_value.str= (char*) wild; + lookup_field_values->db_value.length= strlen(wild); + lookup_field_values->wild_db_value= 1; + } + break; + case SQLCOM_SHOW_TABLES: + case SQLCOM_SHOW_TABLE_STATUS: + case SQLCOM_SHOW_TRIGGERS: + case SQLCOM_SHOW_EVENTS: + lookup_field_values->db_value.str= lex->select_lex.db; + lookup_field_values->db_value.length=strlen(lex->select_lex.db); + if (wild) + { + lookup_field_values->table_value.str= (char*)wild; + lookup_field_values->table_value.length= strlen(wild); + lookup_field_values->wild_table_value= 1; + } + break; + default: + /* + The "default" is for queries over I_S. + All previous cases handle SHOW commands. + */ + calc_lookup_values_from_cond(thd, cond, tables, lookup_field_values); + break; + } +} + + enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table) { return (enum enum_schema_tables) (schema_table - &schema_tables[0]); @@ -2397,81 +2556,87 @@ enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table) idx_field_vals idx_field_vals->db_name contains db name or wild string with_i_schema returns 1 if we added 'IS' name to list - otherwise returns 0 - is_wild_value if value is 1 then idx_field_vals->db_name is - wild string otherwise it's db name; + otherwise returns 0 RETURN zero success non-zero error */ -int make_db_list(THD *thd, List<char> *files, - INDEX_FIELD_VALUES *idx_field_vals, - bool *with_i_schema, bool is_wild_value) +int make_db_list(THD *thd, List<LEX_STRING> *files, + LOOKUP_FIELD_VALUES *lookup_field_vals, + bool *with_i_schema) { - LEX *lex= thd->lex; + LEX_STRING *i_s_name_copy= 0; + i_s_name_copy= thd->make_lex_string(i_s_name_copy, + INFORMATION_SCHEMA_NAME.str, + INFORMATION_SCHEMA_NAME.length, TRUE); *with_i_schema= 0; - get_index_field_values(lex, idx_field_vals); - - if (is_wild_value) + if (lookup_field_vals->wild_db_value) { /* This part of code is only for SHOW DATABASES command. idx_field_vals->db_value can be 0 when we don't use LIKE clause (see also get_index_field_values() function) */ - if (!idx_field_vals->db_value || + if (!lookup_field_vals->db_value.str || !wild_case_compare(system_charset_info, INFORMATION_SCHEMA_NAME.str, - idx_field_vals->db_value)) + lookup_field_vals->db_value.str)) { *with_i_schema= 1; - if (files->push_back(thd->strdup(INFORMATION_SCHEMA_NAME.str))) + if (files->push_back(i_s_name_copy)) return 1; } return (find_files(thd, files, NullS, mysql_data_home, - idx_field_vals->db_value, 1) != FIND_FILES_OK); + lookup_field_vals->db_value.str, 1) != FIND_FILES_OK); } + /* - This part of code is for SHOW TABLES, SHOW TABLE STATUS commands. - idx_field_vals->db_value can't be 0 (see get_index_field_values() - function). + If we have db lookup vaule we just add it to list and + exit from the function */ - if (sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) + if (lookup_field_vals->db_value.str) { if (!my_strcasecmp(system_charset_info, INFORMATION_SCHEMA_NAME.str, - idx_field_vals->db_value)) + lookup_field_vals->db_value.str)) { *with_i_schema= 1; - return files->push_back(thd->strdup(INFORMATION_SCHEMA_NAME.str)); + if (files->push_back(i_s_name_copy)) + return 1; + return 0; } - return files->push_back(thd->strdup(idx_field_vals->db_value)); + if (files->push_back(&lookup_field_vals->db_value)) + return 1; + return 0; } /* Create list of existing databases. It is used in case of select from information schema table */ - if (files->push_back(thd->strdup(INFORMATION_SCHEMA_NAME.str))) + if (files->push_back(i_s_name_copy)) return 1; *with_i_schema= 1; return (find_files(thd, files, NullS, mysql_data_home, NullS, 1) != FIND_FILES_OK); } + struct st_add_schema_table { - List<char> *files; + List<LEX_STRING> *files; const char *wild; }; + static my_bool add_schema_table(THD *thd, plugin_ref plugin, void* p_data) { + LEX_STRING *file_name= 0; st_add_schema_table *data= (st_add_schema_table *)p_data; - List<char> *file_list= data->files; + List<LEX_STRING> *file_list= data->files; const char *wild= data->wild; ST_SCHEMA_TABLE *schema_table= plugin_data(plugin, ST_SCHEMA_TABLE *); DBUG_ENTER("add_schema_table"); @@ -2491,14 +2656,18 @@ static my_bool add_schema_table(THD *thd, plugin_ref plugin, DBUG_RETURN(0); } - if (file_list->push_back(thd->strdup(schema_table->table_name))) - DBUG_RETURN(1); - - DBUG_RETURN(0); + if ((file_name= thd->make_lex_string(file_name, schema_table->table_name, + strlen(schema_table->table_name), + TRUE)) && + !file_list->push_back(file_name)) + DBUG_RETURN(0); + DBUG_RETURN(1); } -int schema_tables_add(THD *thd, List<char> *files, const char *wild) + +int schema_tables_add(THD *thd, List<LEX_STRING> *files, const char *wild) { + LEX_STRING *file_name= 0; ST_SCHEMA_TABLE *tmp_schema_table= schema_tables; st_add_schema_table add_data; DBUG_ENTER("schema_tables_add"); @@ -2519,8 +2688,12 @@ int schema_tables_add(THD *thd, List<char> *files, const char *wild) else if (wild_compare(tmp_schema_table->table_name, wild, 0)) continue; } - if (files->push_back(thd->strdup(tmp_schema_table->table_name))) - DBUG_RETURN(1); + if ((file_name= + thd->make_lex_string(file_name, tmp_schema_table->table_name, + strlen(tmp_schema_table->table_name), TRUE)) && + !files->push_back(file_name)) + continue; + DBUG_RETURN(1); } add_data.files= files; @@ -2533,37 +2706,401 @@ int schema_tables_add(THD *thd, List<char> *files, const char *wild) } +/** + @brief Create table names list + + @details The function creates the list of table names in + database + + @param[in] thd thread handler + @param[in] table_names List of table names in database + @param[in] lex pointer to LEX struct + @param[in] lookup_field_vals pointer to LOOKUP_FIELD_VALUE struct + @param[in] with_i_schema TRUE means that we add I_S tables to list + @param[in] db_name database name + + @return Operation status + @retval 0 ok + @retval 1 fatal error + @retval 2 Not fatal error; Safe to ignore this file list +*/ + +static int +make_table_name_list(THD *thd, List<LEX_STRING> *table_names, LEX *lex, + LOOKUP_FIELD_VALUES *lookup_field_vals, + bool with_i_schema, LEX_STRING *db_name) +{ + char path[FN_REFLEN]; + build_table_filename(path, sizeof(path), db_name->str, "", "", 0); + if (!lookup_field_vals->wild_table_value && + lookup_field_vals->table_value.str) + { + if (with_i_schema) + { + if (find_schema_table(thd, lookup_field_vals->table_value.str)) + { + if (table_names->push_back(&lookup_field_vals->table_value)) + return 1; + } + } + else + { + if (table_names->push_back(&lookup_field_vals->table_value)) + return 1; + /* + Check that table is relevant in current transaction. + (used for ndb engine, see ndbcluster_find_files(), ha_ndbcluster.cc) + */ + VOID(ha_find_files(thd, db_name->str, path, + lookup_field_vals->table_value.str, 0, + table_names)); + } + return 0; + } + + /* + This call will add all matching the wildcards (if specified) IS tables + to the list + */ + if (with_i_schema) + return (schema_tables_add(thd, table_names, + lookup_field_vals->table_value.str)); + + find_files_result res= find_files(thd, table_names, db_name->str, path, + lookup_field_vals->table_value.str, 0); + if (res != FIND_FILES_OK) + { + /* + Downgrade errors about problems with database directory to + warnings if this is not a 'SHOW' command. Another thread + may have dropped database, and we may still have a name + for that directory. + */ + if (res == FIND_FILES_DIR) + { + if (lex->sql_command != SQLCOM_SELECT) + return 1; + thd->clear_error(); + return 2; + } + return 1; + } + return 0; +} + + +/** + @brief Fill I_S table for SHOW COLUMNS|INDEX commands + + @param[in] thd thread handler + @param[in] tables TABLE_LIST for I_S table + @param[in] schema_table pointer to I_S structure + @param[in] open_tables_state_backup pointer to Open_tables_state object + which is used to save|restore original + status of variables related to + open tables state + + @return Operation status + @retval 0 success + @retval 1 error +*/ + +static int +fill_schema_show_cols_or_idxs(THD *thd, TABLE_LIST *tables, + ST_SCHEMA_TABLE *schema_table, + Open_tables_state *open_tables_state_backup) +{ + LEX *lex= thd->lex; + bool res; + LEX_STRING tmp_lex_string, tmp_lex_string1, *db_name, *table_name; + enum_sql_command save_sql_command= lex->sql_command; + TABLE_LIST *show_table_list= (TABLE_LIST*) tables->schema_select_lex-> + table_list.first; + TABLE *table= tables->table; + int error= 1; + DBUG_ENTER("fill_schema_show"); + + lex->all_selects_list= tables->schema_select_lex; + /* + Restore thd->temporary_tables to be able to process + temporary tables(only for 'show index' & 'show columns'). + This should be changed when processing of temporary tables for + I_S tables will be done. + */ + thd->temporary_tables= open_tables_state_backup->temporary_tables; + /* + Let us set fake sql_command so views won't try to merge + themselves into main statement. If we don't do this, + SELECT * from information_schema.xxxx will cause problems. + SQLCOM_SHOW_FIELDS is used because it satisfies 'only_view_structure()' + */ + lex->sql_command= SQLCOM_SHOW_FIELDS; + res= open_normal_and_derived_tables(thd, show_table_list, + MYSQL_LOCK_IGNORE_FLUSH); + lex->sql_command= save_sql_command; + /* + get_all_tables() returns 1 on failure and 0 on success thus + return only these and not the result code of ::process_table() + + We should use show_table_list->alias instead of + show_table_list->table_name because table_name + could be changed during opening of I_S tables. It's safe + to use alias because alias contains original table name + in this case(this part of code is used only for + 'show columns' & 'show statistics' commands). + */ + table_name= thd->make_lex_string(&tmp_lex_string1, show_table_list->alias, + strlen(show_table_list->alias), FALSE); + if (!show_table_list->view) + db_name= thd->make_lex_string(&tmp_lex_string, show_table_list->db, + show_table_list->db_length, FALSE); + else + db_name= &show_table_list->view_db; + + + error= test(schema_table->process_table(thd, show_table_list, + table, res, db_name, + table_name)); + thd->temporary_tables= 0; + close_tables_for_reopen(thd, &show_table_list); + DBUG_RETURN(error); +} + + +/** + @brief Fill I_S table for SHOW TABLE NAMES commands + + @param[in] thd thread handler + @param[in] table TABLE struct for I_S table + @param[in] db_name database name + @param[in] table_name table name + @param[in] with_i_schema I_S table if TRUE + + @return Operation status + @retval 0 success + @retval 1 error +*/ + +static int fill_schema_table_names(THD *thd, TABLE *table, + LEX_STRING *db_name, LEX_STRING *table_name, + bool with_i_schema) +{ + if (with_i_schema) + { + table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), + system_charset_info); + } + else + { + enum legacy_db_type not_used; + char path[FN_REFLEN]; + (void) build_table_filename(path, sizeof(path), db_name->str, + table_name->str, reg_ext, 0); + switch (mysql_frm_type(thd, path, ¬_used)) { + case FRMTYPE_ERROR: + table->field[3]->store(STRING_WITH_LEN("ERROR"), + system_charset_info); + break; + case FRMTYPE_TABLE: + table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), + system_charset_info); + break; + case FRMTYPE_VIEW: + table->field[3]->store(STRING_WITH_LEN("VIEW"), + system_charset_info); + break; + default: + DBUG_ASSERT(0); + } + if (thd->net.last_errno == ER_NO_SUCH_TABLE) + { + thd->clear_error(); + return 0; + } + } + if (schema_table_store_record(thd, table)) + return 1; + return 0; +} + + +/** + @brief Get open table method + + @details The function calculates the method which will be used + for table opening: + SKIP_OPEN_TABLE - do not open table + OPEN_FRM_ONLY - open FRM file only + OPEN_FULL_TABLE - open FRM, data, index files + @param[in] tables I_S table table_list + @param[in] schema_table I_S table struct + @param[in] schema_table_idx I_S table index + + @return return a set of flags + @retval SKIP_OPEN_TABLE | OPEN_FRM_ONLY | OPEN_FULL_TABLE +*/ + +static uint get_table_open_method(TABLE_LIST *tables, + ST_SCHEMA_TABLE *schema_table, + enum enum_schema_tables schema_table_idx) +{ + /* + determine which method will be used for table opening + */ + if (schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE) + { + Field **ptr, *field; + int table_open_method= 0, field_indx= 0; + for (ptr=tables->table->field; (field= *ptr) ; ptr++) + { + if (bitmap_is_set(tables->table->read_set, field->field_index)) + table_open_method|= schema_table->fields_info[field_indx].open_method; + field_indx++; + } + return table_open_method; + } + /* I_S tables which use get_all_tables but can not be optimized */ + return (uint) OPEN_FULL_TABLE; +} + + +/** + @brief Fill I_S table with data from FRM file only + + @param[in] thd thread handler + @param[in] table TABLE struct for I_S table + @param[in] schema_table I_S table struct + @param[in] db_name database name + @param[in] table_name table name + @param[in] schema_table_idx I_S table index + + @return Operation status + @retval 0 Table is processed and we can continue + with new table + @retval 1 It's view and we have to use + open_tables function for this table +*/ + +static int fill_schema_table_from_frm(THD *thd,TABLE *table, + ST_SCHEMA_TABLE *schema_table, + LEX_STRING *db_name, + LEX_STRING *table_name, + enum enum_schema_tables schema_table_idx) +{ + TABLE_SHARE *share; + TABLE tbl; + TABLE_LIST table_list; + uint res= 0; + int error; + char key[MAX_DBKEY_LENGTH]; + uint key_length; + + bzero((char*) &table_list, sizeof(TABLE_LIST)); + bzero((char*) &tbl, sizeof(TABLE)); + + table_list.table_name= table_name->str; + table_list.db= db_name->str; + key_length= create_table_def_key(thd, key, &table_list, 0); + pthread_mutex_lock(&LOCK_open); + share= get_table_share(thd, &table_list, key, + key_length, OPEN_VIEW, &error); + if (!share) + { + res= 0; + goto err; + } + + if (share->is_view) + { + if (schema_table->i_s_requested_object & OPEN_TABLE_ONLY) + { + /* skip view processing */ + res= 0; + goto err1; + } + else if (schema_table->i_s_requested_object & OPEN_VIEW_FULL) + { + /* + tell get_all_tables() to fall back to + open_normal_and_derived_tables() + */ + res= 1; + goto err1; + } + } + + if (share->is_view || + !open_table_from_share(thd, share, table_name->str, 0, + (READ_KEYINFO | COMPUTE_TYPES | + EXTRA_RECORD | OPEN_FRM_FILE_ONLY), + thd->open_options, &tbl, FALSE)) + { + tbl.s= share; + table_list.table= &tbl; + table_list.view= (st_lex*) share->is_view; + res= schema_table->process_table(thd, &table_list, table, + res, db_name, table_name); + closefrm(&tbl, true); + goto err; + } + +err1: + release_table_share(share, RELEASE_NORMAL); + +err: + pthread_mutex_unlock(&LOCK_open); + thd->clear_error(); + return res; +} + + + +/** + @brief Fill I_S tables whose data are retrieved + from frm files and storage engine + + @details The information schema tables are internally represented as + temporary tables that are filled at query execution time. + Those I_S tables whose data are retrieved + from frm files and storage engine are filled by the function + get_all_tables(). + + @param[in] thd thread handler + @param[in] tables I_S table + @param[in] cond 'WHERE' condition + + @return Operation status + @retval 0 success + @retval 1 error +*/ + int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) { LEX *lex= thd->lex; TABLE *table= tables->table; - SELECT_LEX *select_lex= &lex->select_lex; SELECT_LEX *old_all_select_lex= lex->all_selects_list; enum_sql_command save_sql_command= lex->sql_command; SELECT_LEX *lsel= tables->schema_select_lex; ST_SCHEMA_TABLE *schema_table= tables->schema_table; SELECT_LEX sel; - INDEX_FIELD_VALUES idx_field_vals; - char path[FN_REFLEN], *base_name, *orig_base_name, *file_name; - uint len; + LOOKUP_FIELD_VALUES lookup_field_vals; + LEX_STRING *db_name, *table_name; bool with_i_schema; enum enum_schema_tables schema_table_idx; - List<char> bases; - List_iterator_fast<char> it(bases); - COND *partial_cond; + List<LEX_STRING> db_names; + List_iterator_fast<LEX_STRING> it(db_names); + COND *partial_cond= 0; 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; #ifndef NO_EMBEDDED_ACCESS_CHECKS Security_context *sctx= thd->security_ctx; #endif + uint table_open_method; DBUG_ENTER("get_all_tables"); - LINT_INIT(len); - lex->view_prepare_mode= TRUE; lex->reset_n_backup_query_tables_list(&query_tables_list_backup); @@ -2574,183 +3111,160 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) */ thd->reset_n_backup_open_tables_state(&open_tables_state_backup); + /* + this branch processes SHOW FIELDS, SHOW INDEXES commands. + see sql_parse.cc, prepare_schema_table() function where + this values are initialized + */ if (lsel && lsel->table_list.first) { - TABLE_LIST *show_table_list= (TABLE_LIST*) lsel->table_list.first; - bool res; - - lex->all_selects_list= lsel; - /* - Restore thd->temporary_tables to be able to process - temporary tables(only for 'show index' & 'show columns'). - This should be changed when processing of temporary tables for - I_S tables will be done. - */ - thd->temporary_tables= open_tables_state_backup.temporary_tables; - /* - Let us set fake sql_command so views won't try to merge - themselves into main statement. If we don't do this, - SELECT * from information_schema.xxxx will cause problems. - SQLCOM_SHOW_FIELDS is used because it satisfies 'only_view_structure()' - */ - lex->sql_command= SQLCOM_SHOW_FIELDS; - res= open_normal_and_derived_tables(thd, show_table_list, - MYSQL_LOCK_IGNORE_FLUSH); - lex->sql_command= save_sql_command; - /* - get_all_tables() returns 1 on failure and 0 on success thus - return only these and not the result code of ::process_table() - - We should use show_table_list->alias instead of - show_table_list->table_name because table_name - could be changed during opening of I_S tables. It's safe - to use alias because alias contains original table name - in this case(this part of code is used only for - 'show columns' & 'show statistics' commands). - */ - error= test(schema_table->process_table(thd, show_table_list, - table, res, - (show_table_list->view ? - show_table_list->view_db.str : - show_table_list->db), - show_table_list->alias)); - thd->temporary_tables= 0; - close_tables_for_reopen(thd, &show_table_list); + error= fill_schema_show_cols_or_idxs(thd, tables, schema_table, + &open_tables_state_backup); goto err; } schema_table_idx= get_schema_table_idx(schema_table); + get_lookup_field_values(thd, cond, tables, &lookup_field_vals); + DBUG_PRINT("INDEX VALUES",("db_name='%s', table_name='%s'", + lookup_field_vals.db_value.str, + lookup_field_vals.table_value.str)); + + if (!lookup_field_vals.wild_db_value && !lookup_field_vals.wild_table_value) + { + /* + if lookup value is empty string then + it's impossible table name or db name + */ + if (lookup_field_vals.db_value.str && + !lookup_field_vals.db_value.str[0] || + lookup_field_vals.table_value.str && + !lookup_field_vals.table_value.str[0]) + { + error= 0; + goto err; + } + } + + if (lookup_field_vals.db_value.length && + !lookup_field_vals.wild_db_value) + tables->has_db_lookup_value= TRUE; + if (lookup_field_vals.table_value.length && + !lookup_field_vals.wild_table_value) + tables->has_table_lookup_value= TRUE; - if (make_db_list(thd, &bases, &idx_field_vals, - &with_i_schema, 0)) + if (tables->has_db_lookup_value && tables->has_table_lookup_value) + partial_cond= 0; + else + partial_cond= make_cond_for_info_schema(cond, tables); + + tables->table_open_method= table_open_method= + get_table_open_method(tables, schema_table, schema_table_idx); + + if (lex->describe) + { + /* EXPLAIN SELECT */ + error= 0; goto err; + } - partial_cond= make_cond_for_info_schema(cond, tables); + if (make_db_list(thd, &db_names, &lookup_field_vals, &with_i_schema)) + goto err; it.rewind(); /* To get access to new elements in basis list */ - - /* - Below we generate error for non existing database. - (to save old behaviour for SHOW TABLES FROM db) - */ - while ((orig_base_name= base_name= it++) || - ((sql_command_flags[save_sql_command] & CF_SHOW_TABLE_COMMAND) && - (base_name= select_lex->db) && !bases.elements)) + while ((db_name= it++)) { #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (!check_access(thd,SELECT_ACL, base_name, + if (!check_access(thd,SELECT_ACL, db_name->str, &thd->col_access, 0, 1, with_i_schema) || sctx->master_access & (DB_ACLS | SHOW_DB_ACL) || - acl_get(sctx->host, sctx->ip, sctx->priv_user, base_name,0) || - !check_grant_db(thd, base_name)) + acl_get(sctx->host, sctx->ip, sctx->priv_user, db_name->str, 0) || + !check_grant_db(thd, db_name->str)) #endif { - List<char> files; - if (with_i_schema) // information schema table names - { - if (schema_tables_add(thd, &files, idx_field_vals.table_value)) - goto err; - } - else + thd->no_warnings_for_error= 1; + List<LEX_STRING> table_names; + int res= make_table_name_list(thd, &table_names, lex, + &lookup_field_vals, + with_i_schema, db_name); + if (res == 2) /* Not fatal error, continue */ + continue; + if (res) + goto err; + + List_iterator_fast<LEX_STRING> it_files(table_names); + while ((table_name= it_files++)) { - len= build_table_filename(path, sizeof(path), base_name, "", "", 0); - len= FN_LEN - len; - find_files_result res= find_files(thd, &files, base_name, - path, idx_field_vals.table_value, 0); - if (res != FIND_FILES_OK) + restore_record(table, s->default_values); + table->field[schema_table->idx_field1]-> + store(db_name->str, db_name->length, system_charset_info); + table->field[schema_table->idx_field2]-> + store(table_name->str, table_name->length, system_charset_info); + + if (!partial_cond || partial_cond->val_int()) { /* - Downgrade errors about problems with database directory to - warnings if this is not a 'SHOW' command. Another thread - may have dropped database, and we may still have a name - for that directory. + If table is I_S.tables and open_table_method is 0 (eg SKIP_OPEN) + we can skip table opening and we don't have lookup value for + table name or lookup value is wild string(table name list is + already created by make_table_name_list() function). */ - if (res == FIND_FILES_DIR && lex->sql_command == SQLCOM_END) + if (!table_open_method && schema_table_idx == SCH_TABLES && + (!lookup_field_vals.table_value.length || + lookup_field_vals.wild_table_value)) { - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->net.last_errno, thd->net.last_error); - thd->clear_error(); + if (schema_table_store_record(thd, table)) + goto err; /* Out of space in temporary table */ continue; } - else - { - goto err; - } - } - if (lower_case_table_names) - orig_base_name= thd->strdup(base_name); - } - List_iterator_fast<char> it_files(files); - while ((file_name= it_files++)) - { - restore_record(table, s->default_values); - table->field[schema_table->idx_field1]-> - store(base_name, strlen(base_name), system_charset_info); - table->field[schema_table->idx_field2]-> - store(file_name, strlen(file_name),system_charset_info); - if (!partial_cond || partial_cond->val_int()) - { + /* SHOW TABLE NAMES command */ if (schema_table_idx == SCH_TABLE_NAMES) { - if (lex->verbose || - (sql_command_flags[save_sql_command] & CF_STATUS_COMMAND) == 0) - { - if (with_i_schema) - { - table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), - system_charset_info); - } - else - { - 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"), - system_charset_info); - break; - case FRMTYPE_TABLE: - table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), - system_charset_info); - break; - case FRMTYPE_VIEW: - table->field[3]->store(STRING_WITH_LEN("VIEW"), - system_charset_info); - break; - default: - DBUG_ASSERT(0); - } - } - } - if (schema_table_store_record(thd, table)) - goto err; + if (fill_schema_table_names(thd, tables->table, db_name, + table_name, with_i_schema)) + continue; } else { + if (!(table_open_method & ~OPEN_FRM_ONLY) && + !with_i_schema) + { + if (!fill_schema_table_from_frm(thd, table, schema_table, db_name, + table_name, schema_table_idx)) + continue; + } + int res; + LEX_STRING tmp_lex_string, orig_db_name; /* Set the parent lex of 'sel' because it is needed by sel.init_query() which is called inside make_table_list. */ + thd->no_warnings_for_error= 1; sel.parent_lex= lex; - if (make_table_list(thd, &sel, base_name, file_name)) + /* db_name can be changed in make_table_list() func */ + if (!thd->make_lex_string(&orig_db_name, db_name->str, + db_name->length, FALSE)) + goto err; + if (make_table_list(thd, &sel, db_name, table_name)) goto err; TABLE_LIST *show_table_list= (TABLE_LIST*) sel.table_list.first; lex->all_selects_list= &sel; lex->derived_tables= 0; lex->sql_command= SQLCOM_SHOW_FIELDS; + show_table_list->i_s_requested_object= + schema_table->i_s_requested_object; res= open_normal_and_derived_tables(thd, show_table_list, MYSQL_LOCK_IGNORE_FLUSH); lex->sql_command= save_sql_command; - /* - They can drop table after table names list creation and - before table opening. We open non existing table and - get ER_NO_SUCH_TABLE error. In this case we do not store - the record into I_S table and clear error. - */ + if (thd->net.last_errno == ER_NO_SUCH_TABLE) { + /* + Hide error for not existing table. + This error can occur for example when we use + where condition with db name and table name and this + table does not exist. + */ res= 0; thd->clear_error(); } @@ -2763,12 +3277,14 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) to use alias because alias contains original table name in this case. */ + thd->make_lex_string(&tmp_lex_string, show_table_list->alias, + strlen(show_table_list->alias), FALSE); res= schema_table->process_table(thd, show_table_list, table, - res, orig_base_name, - show_table_list->alias); + res, &orig_db_name, + &tmp_lex_string); close_tables_for_reopen(thd, &show_table_list); - DBUG_ASSERT(!lex->query_tables_own_last); } + DBUG_ASSERT(!lex->query_tables_own_last); if (res) goto err; } @@ -2794,27 +3310,27 @@ err: } -bool store_schema_shemata(THD* thd, TABLE *table, const char *db_name, +bool store_schema_shemata(THD* thd, TABLE *table, LEX_STRING *db_name, CHARSET_INFO *cs) { restore_record(table, s->default_values); - table->field[1]->store(db_name, strlen(db_name), system_charset_info); + table->field[1]->store(db_name->str, db_name->length, system_charset_info); table->field[2]->store(cs->csname, strlen(cs->csname), system_charset_info); table->field[3]->store(cs->name, strlen(cs->name), system_charset_info); return schema_table_store_record(thd, table); } -int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond) +int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond) { /* TODO: fill_schema_shemata() is called when new client is connected. Returning error status in this case leads to client hangup. */ - INDEX_FIELD_VALUES idx_field_vals; - List<char> files; - char *file_name; + LOOKUP_FIELD_VALUES lookup_field_vals; + List<LEX_STRING> db_names; + LEX_STRING *db_name; bool with_i_schema; HA_CREATE_INFO create; TABLE *table= tables->table; @@ -2823,16 +3339,37 @@ int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond) #endif DBUG_ENTER("fill_schema_shemata"); - if (make_db_list(thd, &files, &idx_field_vals, - &with_i_schema, 1)) + get_lookup_field_values(thd, cond, tables, &lookup_field_vals); + DBUG_PRINT("INDEX VALUES",("db_name='%s', table_name='%s'", + lookup_field_vals.db_value.str, + lookup_field_vals.table_value.str)); + if (make_db_list(thd, &db_names, &lookup_field_vals, + &with_i_schema)) DBUG_RETURN(1); - List_iterator_fast<char> it(files); - while ((file_name=it++)) + /* + If we have lookup db value we should check that the database exists + */ + if(lookup_field_vals.db_value.str && !lookup_field_vals.wild_db_value) + { + char path[FN_REFLEN+16]; + uint path_len; + MY_STAT stat_info; + if (!lookup_field_vals.db_value.str[0]) + DBUG_RETURN(0); + path_len= build_table_filename(path, sizeof(path), + lookup_field_vals.db_value.str, "", "", 0); + path[path_len-1]= 0; + if (!my_stat(path,&stat_info,MYF(0))) + DBUG_RETURN(0); + } + + List_iterator_fast<LEX_STRING> it(db_names); + while ((db_name=it++)) { if (with_i_schema) // information schema name is always first in list { - if (store_schema_shemata(thd, table, file_name, + if (store_schema_shemata(thd, table, db_name, system_charset_info)) DBUG_RETURN(1); with_i_schema= 0; @@ -2840,13 +3377,12 @@ int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond) } #ifndef NO_EMBEDDED_ACCESS_CHECKS if (sctx->master_access & (DB_ACLS | SHOW_DB_ACL) || - acl_get(sctx->host, sctx->ip, sctx->priv_user, file_name,0) || - !check_grant_db(thd, file_name)) + acl_get(sctx->host, sctx->ip, sctx->priv_user, db_name->str, 0) || + !check_grant_db(thd, db_name->str)) #endif { - load_db_opt_by_name(thd, file_name, &create); - - if (store_schema_shemata(thd, table, file_name, + load_db_opt_by_name(thd, db_name->str, &create); + if (store_schema_shemata(thd, table, db_name, create.default_table_charset)) DBUG_RETURN(1); } @@ -2855,10 +3391,10 @@ int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond) } -static int get_schema_tables_record(THD *thd, struct st_table_list *tables, +static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, - const char *base_name, - const char *file_name) + LEX_STRING *db_name, + LEX_STRING *table_name) { const char *tmp_buff; MYSQL_TIME time; @@ -2866,8 +3402,8 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables, DBUG_ENTER("get_schema_tables_record"); restore_record(table, s->default_values); - table->field[1]->store(base_name, strlen(base_name), cs); - table->field[2]->store(file_name, strlen(file_name), cs); + table->field[1]->store(db_name->str, db_name->length, cs); + table->field[2]->store(table_name->str, table_name->length, cs); if (res) { /* @@ -2890,12 +3426,11 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables, } else { + char option_buff[350],*ptr; TABLE *show_table= tables->table; TABLE_SHARE *share= show_table->s; handler *file= show_table->file; - - file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_AUTO | - HA_STATUS_NO_LOCK); + handlerton *tmp_db_type= share->db_type(); if (share->tmp_table == SYSTEM_TMP_TABLE) table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), cs); else if (share->tmp_table) @@ -2909,89 +3444,15 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables, continue; table->field[i]->set_notnull(); } - tmp_buff= file->table_type(); +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (share->db_type() == partition_hton && + share->partition_info_len) + tmp_db_type= share->default_part_db_type; +#endif + tmp_buff= (char *) ha_resolve_storage_engine_name(tmp_db_type); table->field[4]->store(tmp_buff, strlen(tmp_buff), cs); table->field[5]->store((longlong) share->frm_version, TRUE); - enum row_type row_type = file->get_row_type(); - switch (row_type) { - case ROW_TYPE_NOT_USED: - case ROW_TYPE_DEFAULT: - tmp_buff= ((share->db_options_in_use & - HA_OPTION_COMPRESS_RECORD) ? "Compressed" : - (share->db_options_in_use & HA_OPTION_PACK_RECORD) ? - "Dynamic" : "Fixed"); - break; - case ROW_TYPE_FIXED: - tmp_buff= "Fixed"; - break; - case ROW_TYPE_DYNAMIC: - tmp_buff= "Dynamic"; - break; - case ROW_TYPE_COMPRESSED: - tmp_buff= "Compressed"; - break; - case ROW_TYPE_REDUNDANT: - tmp_buff= "Redundant"; - break; - case ROW_TYPE_COMPACT: - tmp_buff= "Compact"; - break; - case ROW_TYPE_PAGE: - tmp_buff= "Page"; - break; - } - table->field[6]->store(tmp_buff, strlen(tmp_buff), cs); - if (!tables->schema_table) - { - table->field[7]->store((longlong) file->stats.records, TRUE); - table->field[7]->set_notnull(); - } - table->field[8]->store((longlong) file->stats.mean_rec_length, TRUE); - table->field[9]->store((longlong) file->stats.data_file_length, TRUE); - if (file->stats.max_data_file_length) - { - table->field[10]->store((longlong) file->stats.max_data_file_length, - TRUE); - } - table->field[11]->store((longlong) file->stats.index_file_length, TRUE); - table->field[12]->store((longlong) file->stats.delete_length, TRUE); - if (show_table->found_next_number_field) - { - table->field[13]->store((longlong) file->stats.auto_increment_value, - TRUE); - table->field[13]->set_notnull(); - } - if (file->stats.create_time) - { - thd->variables.time_zone->gmt_sec_to_TIME(&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, - (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, - (my_time_t) file->stats.check_time); - table->field[16]->store_time(&time, MYSQL_TIMESTAMP_DATETIME); - table->field[16]->set_notnull(); - } - tmp_buff= (share->table_charset ? - share->table_charset->name : "default"); - table->field[17]->store(tmp_buff, strlen(tmp_buff), cs); - if (file->ha_table_flags() & (ulong) HA_HAS_CHECKSUM) - { - table->field[18]->store((longlong) file->checksum(), TRUE); - table->field[18]->set_notnull(); - } - char option_buff[350],*ptr; ptr=option_buff; if (share->min_rows) { @@ -3024,6 +3485,12 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables, ptr=strxmov(ptr, " row_format=", ha_row_type[(uint) share->row_type], NullS); + if (share->transactional != HA_CHOICE_UNDEF) + { + ptr= strxmov(ptr, " TRANSACTIONAL=", + (share->transactional == HA_CHOICE_YES ? "1" : "0"), + NullS); + } #ifdef WITH_PARTITION_STORAGE_ENGINE if (show_table->s->db_type() == partition_hton && show_table->part_info != NULL && @@ -3036,17 +3503,91 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables, table->field[19]->store(option_buff+1, (ptr == option_buff ? 0 : (uint) (ptr-option_buff)-1), cs); + + if (share->comment.str) + table->field[20]->store(share->comment.str, share->comment.length, cs); + + if(file) { - char *comment; - comment= show_table->file->update_table_comment(share->comment.str); - if (comment) + file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_AUTO | + HA_STATUS_NO_LOCK); + enum row_type row_type = file->get_row_type(); + switch (row_type) { + case ROW_TYPE_NOT_USED: + case ROW_TYPE_DEFAULT: + tmp_buff= ((share->db_options_in_use & + HA_OPTION_COMPRESS_RECORD) ? "Compressed" : + (share->db_options_in_use & HA_OPTION_PACK_RECORD) ? + "Dynamic" : "Fixed"); + break; + case ROW_TYPE_FIXED: + tmp_buff= "Fixed"; + break; + case ROW_TYPE_DYNAMIC: + tmp_buff= "Dynamic"; + break; + case ROW_TYPE_COMPRESSED: + tmp_buff= "Compressed"; + break; + case ROW_TYPE_REDUNDANT: + tmp_buff= "Redundant"; + break; + case ROW_TYPE_COMPACT: + tmp_buff= "Compact"; + break; + case ROW_TYPE_PAGE: + tmp_buff= "Paged"; + break; + } + table->field[6]->store(tmp_buff, strlen(tmp_buff), cs); + if (!tables->schema_table) + { + table->field[7]->store((longlong) file->stats.records, TRUE); + table->field[7]->set_notnull(); + } + table->field[8]->store((longlong) file->stats.mean_rec_length, TRUE); + table->field[9]->store((longlong) file->stats.data_file_length, TRUE); + if (file->stats.max_data_file_length) + { + table->field[10]->store((longlong) file->stats.max_data_file_length, + TRUE); + } + table->field[11]->store((longlong) file->stats.index_file_length, TRUE); + table->field[12]->store((longlong) file->stats.delete_length, TRUE); + if (show_table->found_next_number_field) + { + table->field[13]->store((longlong) file->stats.auto_increment_value, + TRUE); + table->field[13]->set_notnull(); + } + if (file->stats.create_time) + { + thd->variables.time_zone->gmt_sec_to_TIME(&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, + (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, + (my_time_t) file->stats.check_time); + table->field[16]->store_time(&time, MYSQL_TIMESTAMP_DATETIME); + table->field[16]->set_notnull(); + } + tmp_buff= (share->table_charset ? + share->table_charset->name : "default"); + table->field[17]->store(tmp_buff, strlen(tmp_buff), cs); + if (file->ha_table_flags() & (ulong) HA_HAS_CHECKSUM) { - table->field[20]->store(comment, - (comment == share->comment.str ? - share->comment.length : - strlen(comment)), cs); - if (comment != share->comment.str) - my_free(comment, MYF(0)); + table->field[18]->store((longlong) file->checksum(), TRUE); + table->field[18]->set_notnull(); } } } @@ -3054,19 +3595,17 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables, } -static int get_schema_column_record(THD *thd, struct st_table_list *tables, +static int get_schema_column_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, - const char *base_name, - const char *file_name) + LEX_STRING *db_name, + LEX_STRING *table_name) { LEX *lex= thd->lex; const char *wild= lex->wild ? lex->wild->ptr() : NullS; CHARSET_INFO *cs= system_charset_info; TABLE *show_table; - handler *file; Field **ptr,*field; int count; - uint base_name_length, file_name_length; DBUG_ENTER("get_schema_column_record"); if (res) @@ -3086,22 +3625,17 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, } show_table= tables->table; - file= show_table->file; count= 0; - file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); restore_record(show_table, s->default_values); - base_name_length= strlen(base_name); - file_name_length= strlen(file_name); show_table->use_all_columns(); // Required for default - for (ptr=show_table->field; (field= *ptr) ; ptr++) + for (ptr= show_table->field; (field= *ptr) ; ptr++) { const char *tmp_buff; uchar *pos; bool is_blob; uint flags=field->flags; char tmp[MAX_FIELD_WIDTH]; - char tmp1[MAX_FIELD_WIDTH]; String type(tmp,sizeof(tmp), system_charset_info); char *end; int decimals, field_length; @@ -3117,13 +3651,12 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, #ifndef NO_EMBEDDED_ACCESS_CHECKS uint col_access; - check_access(thd,SELECT_ACL | EXTRA_ACL, base_name, + check_access(thd,SELECT_ACL | EXTRA_ACL, db_name->str, &tables->grant.privilege, 0, 0, test(tables->schema_table)); col_access= get_column_grant(thd, &tables->grant, - base_name, file_name, + db_name->str, table_name->str, field->field_name) & COL_ACLS; - if (lex->sql_command != SQLCOM_SHOW_FIELDS && - !tables->schema_table && !col_access) + if (!tables->schema_table && !col_access) continue; end= tmp; for (uint bitnr=0; col_access ; col_access>>=1,bitnr++) @@ -3137,8 +3670,8 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, table->field[17]->store(tmp+1,end == tmp ? 0 : (uint) (end-tmp-1), cs); #endif - table->field[1]->store(base_name, base_name_length, cs); - table->field[2]->store(file_name, file_name_length, cs); + table->field[1]->store(db_name->str, db_name->length, cs); + table->field[2]->store(table_name->str, table_name->length, cs); table->field[3]->store(field->field_name, strlen(field->field_name), cs); table->field[4]->store((longlong) count, TRUE); @@ -3148,31 +3681,10 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, table->field[7]->store(type.ptr(), (tmp_buff ? tmp_buff - type.ptr() : type.length()), cs); - if (show_table->timestamp_field == field && - field->unireg_check != Field::TIMESTAMP_UN_FIELD) - { - table->field[5]->store(STRING_WITH_LEN("CURRENT_TIMESTAMP"), cs); - table->field[5]->set_notnull(); - } - else if (field->unireg_check != Field::NEXT_NUMBER && - !field->is_null() && - !(field->flags & NO_DEFAULT_VALUE_FLAG)) - { - String def(tmp1,sizeof(tmp1), cs); - type.set(tmp, sizeof(tmp), field->charset()); - field->val_str(&type); - uint dummy_errors; - def.copy(type.ptr(), type.length(), type.charset(), cs, &dummy_errors); - table->field[5]->store(def.ptr(), def.length(), def.charset()); - table->field[5]->set_notnull(); - } - else if (field->unireg_check == Field::NEXT_NUMBER || - lex->sql_command != SQLCOM_SHOW_FIELDS || - field->maybe_null()) - table->field[5]->set_null(); // Null as default - else + + if (get_field_default_value(thd, show_table, field, &type, 0)) { - table->field[5]->store("",0, cs); + table->field[5]->store(type.ptr(), type.length(), cs); table->field[5]->set_notnull(); } pos=(uchar*) ((flags & NOT_NULL_FLAG) ? "NO" : "YES"); @@ -3561,10 +4073,10 @@ err: } -static int get_schema_stat_record(THD *thd, struct st_table_list *tables, +static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, - const char *base_name, - const char *file_name) + LEX_STRING *db_name, + LEX_STRING *table_name) { CHARSET_INFO *cs= system_charset_info; DBUG_ENTER("get_schema_stat_record"); @@ -3576,7 +4088,7 @@ static int get_schema_stat_record(THD *thd, struct st_table_list *tables, I.e. we are in SELECT FROM INFORMATION_SCHEMA.STATISTICS rather than in SHOW KEYS */ - if (!tables->view) + if (thd->net.last_errno) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, thd->net.last_errno, thd->net.last_error); thd->clear_error(); @@ -3587,10 +4099,11 @@ static int get_schema_stat_record(THD *thd, struct st_table_list *tables, else if (!tables->view) { TABLE *show_table= tables->table; - KEY *key_info=show_table->key_info; - show_table->file->info(HA_STATUS_VARIABLE | - HA_STATUS_NO_LOCK | - HA_STATUS_TIME); + KEY *key_info=show_table->s->key_info; + if (show_table->file) + show_table->file->info(HA_STATUS_VARIABLE | + HA_STATUS_NO_LOCK | + HA_STATUS_TIME); for (uint i=0 ; i < show_table->s->keys ; i++,key_info++) { KEY_PART_INFO *key_part= key_info->key_part; @@ -3598,35 +4111,40 @@ static int get_schema_stat_record(THD *thd, struct st_table_list *tables, for (uint j=0 ; j < key_info->key_parts ; j++,key_part++) { restore_record(table, s->default_values); - table->field[1]->store(base_name, strlen(base_name), cs); - table->field[2]->store(file_name, strlen(file_name), cs); + table->field[1]->store(db_name->str, db_name->length, cs); + table->field[2]->store(table_name->str, table_name->length, cs); table->field[3]->store((longlong) ((key_info->flags & HA_NOSAME) ? 0 : 1), TRUE); - table->field[4]->store(base_name, strlen(base_name), cs); + table->field[4]->store(db_name->str, db_name->length, cs); table->field[5]->store(key_info->name, strlen(key_info->name), cs); table->field[6]->store((longlong) (j+1), TRUE); str=(key_part->field ? key_part->field->field_name : "?unknown field?"); table->field[7]->store(str, strlen(str), cs); - if (show_table->file->index_flags(i, j, 0) & HA_READ_ORDER) + if (show_table->file) { - table->field[8]->store(((key_part->key_part_flag & - HA_REVERSE_SORT) ? - "D" : "A"), 1, cs); - table->field[8]->set_notnull(); - } - KEY *key=show_table->key_info+i; - if (key->rec_per_key[j]) - { - ha_rows records=(show_table->file->stats.records / - key->rec_per_key[j]); - table->field[9]->store((longlong) records, TRUE); - table->field[9]->set_notnull(); + if (show_table->file->index_flags(i, j, 0) & HA_READ_ORDER) + { + table->field[8]->store(((key_part->key_part_flag & + HA_REVERSE_SORT) ? + "D" : "A"), 1, cs); + table->field[8]->set_notnull(); + } + KEY *key=show_table->key_info+i; + if (key->rec_per_key[j]) + { + ha_rows records=(show_table->file->stats.records / + key->rec_per_key[j]); + table->field[9]->store((longlong) records, TRUE); + table->field[9]->set_notnull(); + } + str= show_table->file->index_type(i); + table->field[13]->store(str, strlen(str), cs); } if (!(key_info->flags & HA_FULLTEXT) && (key_part->field && key_part->length != - show_table->field[key_part->fieldnr-1]->key_length())) + show_table->s->field[key_part->fieldnr-1]->key_length())) { table->field[10]->store((longlong) key_part->length / key_part->field->charset()->mbmaxlen, TRUE); @@ -3635,8 +4153,6 @@ static int get_schema_stat_record(THD *thd, struct st_table_list *tables, uint flags= key_part->field ? key_part->field->flags : 0; const char *pos=(char*) ((flags & NOT_NULL_FLAG) ? "" : "YES"); table->field[12]->store(pos, strlen(pos), cs); - pos= show_table->file->index_type(i); - table->field[13]->store(pos, strlen(pos), cs); if (!show_table->s->keys_in_use.is_set(i)) table->field[14]->store(STRING_WITH_LEN("disabled"), cs); else @@ -3651,21 +4167,30 @@ static int get_schema_stat_record(THD *thd, struct st_table_list *tables, } -static int get_schema_views_record(THD *thd, struct st_table_list *tables, +static int get_schema_views_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, - const char *base_name, - const char *file_name) + LEX_STRING *db_name, + LEX_STRING *table_name) { CHARSET_INFO *cs= system_charset_info; DBUG_ENTER("get_schema_views_record"); + LEX_STRING *tmp_db_name, *tmp_table_name; char definer[USER_HOST_BUFF_SIZE]; uint definer_len; bool updatable_view; + /* + if SELECT FROM I_S.VIEWS uses only fields + which have OPEN_FRM_ONLY flag then 'tables' + structure is zeroed and only tables->view is set. + (see fill_schema_table_from_frm() function). + So we should disable other fields filling. + */ + bool only_share= !tables->definer.user.str; if (tables->view) { Security_context *sctx= thd->security_ctx; - if (!tables->allowed_show) + if (!only_share && !tables->allowed_show) { if (!my_strcasecmp(system_charset_info, tables->definer.user.str, sctx->priv_user) && @@ -3674,77 +4199,88 @@ static int get_schema_views_record(THD *thd, struct st_table_list *tables, tables->allowed_show= TRUE; } restore_record(table, s->default_values); - table->field[1]->store(tables->view_db.str, tables->view_db.length, cs); - table->field[2]->store(tables->view_name.str, tables->view_name.length, cs); - if (tables->allowed_show) + tmp_db_name= &tables->view_db; + tmp_table_name= &tables->view_name; + if (only_share) { - table->field[3]->store(tables->view_body_utf8.str, - tables->view_body_utf8.length, - cs); + tmp_db_name= db_name; + tmp_table_name= table_name; } - - if (tables->with_check != VIEW_CHECK_NONE) + table->field[1]->store(tmp_db_name->str, tmp_db_name->length, cs); + table->field[2]->store(tmp_table_name->str, tmp_table_name->length, cs); + if (!only_share) { - if (tables->with_check == VIEW_CHECK_LOCAL) - table->field[4]->store(STRING_WITH_LEN("LOCAL"), cs); + if (tables->allowed_show) + { + table->field[3]->store(tables->view_body_utf8.str, + tables->view_body_utf8.length, + cs); + } + + if (tables->with_check != VIEW_CHECK_NONE) + { + if (tables->with_check == VIEW_CHECK_LOCAL) + table->field[4]->store(STRING_WITH_LEN("LOCAL"), cs); + else + table->field[4]->store(STRING_WITH_LEN("CASCADED"), cs); + } else - table->field[4]->store(STRING_WITH_LEN("CASCADED"), cs); - } - else - table->field[4]->store(STRING_WITH_LEN("NONE"), cs); + table->field[4]->store(STRING_WITH_LEN("NONE"), cs); - updatable_view= 0; - if (tables->algorithm != VIEW_ALGORITHM_TMPTABLE) - { - /* - We should use tables->view->select_lex.item_list here and - can not use Field_iterator_view because the view always uses - temporary algorithm during opening for I_S and - TABLE_LIST fields 'field_translation' & 'field_translation_end' - are uninitialized is this case. - */ - List<Item> *fields= &tables->view->select_lex.item_list; - List_iterator<Item> it(*fields); - Item *item; - Item_field *field; - /* - chech that at least one coulmn in view is updatable - */ - while ((item= it++)) + updatable_view= 0; + if (tables->algorithm != VIEW_ALGORITHM_TMPTABLE) { - if ((field= item->filed_for_view_update()) && field->field && - !field->field->table->pos_in_table_list->schema_table) + /* + We should use tables->view->select_lex.item_list here and + can not use Field_iterator_view because the view always uses + temporary algorithm during opening for I_S and + TABLE_LIST fields 'field_translation' & 'field_translation_end' + are uninitialized is this case. + */ + List<Item> *fields= &tables->view->select_lex.item_list; + List_iterator<Item> it(*fields); + Item *item; + Item_field *field; + /* + check that at least one column in view is updatable + */ + while ((item= it++)) { - updatable_view= 1; - break; + if ((field= item->filed_for_view_update()) && field->field && + !field->field->table->pos_in_table_list->schema_table) + { + updatable_view= 1; + break; + } } + if (updatable_view && !tables->view->can_be_merged()) + updatable_view= 0; } - } - if (updatable_view) - table->field[5]->store(STRING_WITH_LEN("YES"), cs); - else - table->field[5]->store(STRING_WITH_LEN("NO"), cs); - definer_len= (strxmov(definer, tables->definer.user.str, "@", - tables->definer.host.str, NullS) - definer); - table->field[6]->store(definer, definer_len, cs); - if (tables->view_suid) - table->field[7]->store(STRING_WITH_LEN("DEFINER"), cs); - else - table->field[7]->store(STRING_WITH_LEN("INVOKER"), cs); + if (updatable_view) + table->field[5]->store(STRING_WITH_LEN("YES"), cs); + else + table->field[5]->store(STRING_WITH_LEN("NO"), cs); + definer_len= (strxmov(definer, tables->definer.user.str, "@", + tables->definer.host.str, NullS) - definer); + table->field[6]->store(definer, definer_len, cs); + if (tables->view_suid) + table->field[7]->store(STRING_WITH_LEN("DEFINER"), cs); + else + table->field[7]->store(STRING_WITH_LEN("INVOKER"), cs); - table->field[8]->store( - tables->view_creation_ctx->get_client_cs()->csname, - strlen(tables->view_creation_ctx->get_client_cs()->csname), - cs); + table->field[8]->store(tables->view_creation_ctx->get_client_cs()->csname, + strlen(tables->view_creation_ctx-> + get_client_cs()->csname), cs); - table->field[9]->store( - tables->view_creation_ctx->get_connection_cl()->name, - strlen(tables->view_creation_ctx->get_connection_cl()->name), - cs); + table->field[9]->store(tables->view_creation_ctx-> + get_connection_cl()->name, + strlen(tables->view_creation_ctx-> + get_connection_cl()->name), cs); + } if (schema_table_store_record(thd, table)) DBUG_RETURN(1); - if (res) + if (res && thd->net.last_errno) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, thd->net.last_errno, thd->net.last_error); } @@ -3754,30 +4290,30 @@ static int get_schema_views_record(THD *thd, struct st_table_list *tables, } -bool store_constraints(THD *thd, TABLE *table, const char *db, - const char *tname, const char *key_name, +bool store_constraints(THD *thd, TABLE *table, LEX_STRING *db_name, + LEX_STRING *table_name, const char *key_name, uint key_len, const char *con_type, uint con_len) { CHARSET_INFO *cs= system_charset_info; restore_record(table, s->default_values); - table->field[1]->store(db, strlen(db), cs); + table->field[1]->store(db_name->str, db_name->length, cs); table->field[2]->store(key_name, key_len, cs); - table->field[3]->store(db, strlen(db), cs); - table->field[4]->store(tname, strlen(tname), cs); + table->field[3]->store(db_name->str, db_name->length, cs); + table->field[4]->store(table_name->str, table_name->length, cs); table->field[5]->store(con_type, con_len, cs); return schema_table_store_record(thd, table); } -static int get_schema_constraints_record(THD *thd, struct st_table_list *tables, +static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, - const char *base_name, - const char *file_name) + LEX_STRING *db_name, + LEX_STRING *table_name) { DBUG_ENTER("get_schema_constraints_record"); if (res) { - if (!tables->view) + if (thd->net.last_errno) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, thd->net.last_errno, thd->net.last_error); thd->clear_error(); @@ -3799,14 +4335,14 @@ static int get_schema_constraints_record(THD *thd, struct st_table_list *tables, if (i == primary_key && !strcmp(key_info->name, primary_key_name)) { - if (store_constraints(thd, table, base_name, file_name, key_info->name, + if (store_constraints(thd, table, db_name, table_name, key_info->name, strlen(key_info->name), STRING_WITH_LEN("PRIMARY KEY"))) DBUG_RETURN(1); } else if (key_info->flags & HA_NOSAME) { - if (store_constraints(thd, table, base_name, file_name, key_info->name, + if (store_constraints(thd, table, db_name, table_name, key_info->name, strlen(key_info->name), STRING_WITH_LEN("UNIQUE"))) DBUG_RETURN(1); @@ -3818,7 +4354,7 @@ static int get_schema_constraints_record(THD *thd, struct st_table_list *tables, List_iterator_fast<FOREIGN_KEY_INFO> it(f_key_list); while ((f_key_info=it++)) { - if (store_constraints(thd, table, base_name, file_name, + if (store_constraints(thd, table, db_name, table_name, f_key_info->forein_id->str, strlen(f_key_info->forein_id->str), "FOREIGN KEY", 11)) @@ -3829,8 +4365,8 @@ static int get_schema_constraints_record(THD *thd, struct st_table_list *tables, } -static bool store_trigger(THD *thd, TABLE *table, const char *db, - const char *tname, LEX_STRING *trigger_name, +static bool store_trigger(THD *thd, TABLE *table, LEX_STRING *db_name, + LEX_STRING *table_name, LEX_STRING *trigger_name, enum trg_event_type event, enum trg_action_time_type timing, LEX_STRING *trigger_stmt, @@ -3844,12 +4380,12 @@ static bool store_trigger(THD *thd, TABLE *table, const char *db, LEX_STRING sql_mode_rep; restore_record(table, s->default_values); - table->field[1]->store(db, strlen(db), cs); + table->field[1]->store(db_name->str, db_name->length, cs); table->field[2]->store(trigger_name->str, trigger_name->length, cs); table->field[3]->store(trg_event_type_names[event].str, trg_event_type_names[event].length, cs); - table->field[5]->store(db, strlen(db), cs); - table->field[6]->store(tname, strlen(tname), cs); + table->field[5]->store(db_name->str, db_name->length, cs); + table->field[6]->store(table_name->str, table_name->length, cs); table->field[9]->store(trigger_stmt->str, trigger_stmt->length, cs); table->field[10]->store(STRING_WITH_LEN("ROW"), cs); table->field[11]->store(trg_action_time_type_names[timing].str, @@ -3870,10 +4406,10 @@ static bool store_trigger(THD *thd, TABLE *table, const char *db, } -static int get_schema_triggers_record(THD *thd, struct st_table_list *tables, +static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, - const char *base_name, - const char *file_name) + LEX_STRING *db_name, + LEX_STRING *table_name) { DBUG_ENTER("get_schema_triggers_record"); /* @@ -3882,7 +4418,7 @@ static int get_schema_triggers_record(THD *thd, struct st_table_list *tables, */ if (res) { - if (!tables->view) + if (thd->net.last_errno) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, thd->net.last_errno, thd->net.last_error); thd->clear_error(); @@ -3892,6 +4428,12 @@ static int get_schema_triggers_record(THD *thd, struct st_table_list *tables, { Table_triggers_list *triggers= tables->table->triggers; int event, timing; + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_table_access(thd, TRIGGER_ACL, tables, 1)) + goto ret; +#endif + for (event= 0; event < (int)TRG_EVENT_MAX; event++) { for (timing= 0; timing < (int)TRG_ACTION_MAX; timing++) @@ -3916,7 +4458,7 @@ static int get_schema_triggers_record(THD *thd, struct st_table_list *tables, &db_cl_name)) continue; - if (store_trigger(thd, table, base_name, file_name, &trigger_name, + if (store_trigger(thd, table, db_name, table_name, &trigger_name, (enum trg_event_type) event, (enum trg_action_time_type) timing, &trigger_stmt, sql_mode, @@ -3928,34 +4470,38 @@ static int get_schema_triggers_record(THD *thd, struct st_table_list *tables, } } } +#ifndef NO_EMBEDDED_ACCESS_CHECKS +ret: +#endif DBUG_RETURN(0); } -void store_key_column_usage(TABLE *table, const char*db, const char *tname, - const char *key_name, uint key_len, - const char *con_type, uint con_len, longlong idx) +void store_key_column_usage(TABLE *table, LEX_STRING *db_name, + LEX_STRING *table_name, const char *key_name, + uint key_len, const char *con_type, uint con_len, + longlong idx) { CHARSET_INFO *cs= system_charset_info; - table->field[1]->store(db, strlen(db), cs); + table->field[1]->store(db_name->str, db_name->length, cs); table->field[2]->store(key_name, key_len, cs); - table->field[4]->store(db, strlen(db), cs); - table->field[5]->store(tname, strlen(tname), cs); + table->field[4]->store(db_name->str, db_name->length, cs); + table->field[5]->store(table_name->str, table_name->length, cs); table->field[6]->store(con_type, con_len, cs); table->field[7]->store((longlong) idx, TRUE); } static int get_schema_key_column_usage_record(THD *thd, - struct st_table_list *tables, + TABLE_LIST *tables, TABLE *table, bool res, - const char *base_name, - const char *file_name) + LEX_STRING *db_name, + LEX_STRING *table_name) { DBUG_ENTER("get_schema_key_column_usage_record"); if (res) { - if (!tables->view) + if (thd->net.last_errno) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, thd->net.last_errno, thd->net.last_error); thd->clear_error(); @@ -3982,7 +4528,7 @@ static int get_schema_key_column_usage_record(THD *thd, { f_idx++; restore_record(table, s->default_values); - store_key_column_usage(table, base_name, file_name, + store_key_column_usage(table, db_name, table_name, key_info->name, strlen(key_info->name), key_part->field->field_name, @@ -4009,7 +4555,7 @@ static int get_schema_key_column_usage_record(THD *thd, r_info= it1++; f_idx++; restore_record(table, s->default_values); - store_key_column_usage(table, base_name, file_name, + store_key_column_usage(table, db_name, table_name, f_key_info->forein_id->str, f_key_info->forein_id->length, f_info->str, f_info->length, @@ -4132,10 +4678,10 @@ static void store_schema_partitions_record(THD *thd, TABLE *schema_table, } -static int get_schema_partitions_record(THD *thd, struct st_table_list *tables, +static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, - const char *base_name, - const char *file_name) + LEX_STRING *db_name, + LEX_STRING *table_name) { CHARSET_INFO *cs= system_charset_info; char buff[61]; @@ -4150,7 +4696,7 @@ static int get_schema_partitions_record(THD *thd, struct st_table_list *tables, if (res) { - if (!tables->view) + if (thd->net.last_errno) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, thd->net.last_errno, thd->net.last_error); thd->clear_error(); @@ -4166,8 +4712,8 @@ static int get_schema_partitions_record(THD *thd, struct st_table_list *tables, uint part_pos= 0, part_id= 0; restore_record(table, s->default_values); - table->field[1]->store(base_name, strlen(base_name), cs); - table->field[2]->store(file_name, strlen(file_name), cs); + table->field[1]->store(db_name->str, db_name->length, cs); + table->field[2]->store(table_name->str, table_name->length, cs); /* Partition method*/ @@ -4678,16 +5224,16 @@ int fill_status(THD *thd, TABLE_LIST *tables, COND *cond) */ static int -get_referential_constraints_record(THD *thd, struct st_table_list *tables, +get_referential_constraints_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, - const char *base_name, const char *file_name) + LEX_STRING *db_name, LEX_STRING *table_name) { CHARSET_INFO *cs= system_charset_info; DBUG_ENTER("get_referential_constraints_record"); if (res) { - if (!tables->view) + if (thd->net.last_errno) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, thd->net.last_errno, thd->net.last_error); thd->clear_error(); @@ -4707,8 +5253,8 @@ get_referential_constraints_record(THD *thd, struct st_table_list *tables, while ((f_key_info= it++)) { restore_record(table, s->default_values); - table->field[1]->store(base_name, strlen(base_name), cs); - table->field[9]->store(file_name, strlen(file_name), cs); + table->field[1]->store(db_name->str, db_name->length, cs); + table->field[9]->store(table_name->str, table_name->length, cs); table->field[2]->store(f_key_info->forein_id->str, f_key_info->forein_id->length, cs); table->field[4]->store(f_key_info->referenced_db->str, @@ -4778,7 +5324,7 @@ static my_bool find_schema_table_in_plugin(THD *thd, plugin_ref plugin, RETURN 0 table not found - # pointer to 'shema_tables' element + # pointer to 'schema_tables' element */ ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name) @@ -4921,6 +5467,12 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) TMP_TABLE_ALL_COLUMNS), HA_POS_ERROR, table_list->alias))) DBUG_RETURN(0); + my_bitmap_map* bitmaps= + (my_bitmap_map*) thd->alloc(bitmap_buffer_size(field_count)); + bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count, + FALSE); + table->read_set= &table->def_read_set; + bitmap_clear_all(table->read_set); table_list->schema_table_param= tmp_table_param; DBUG_RETURN(table); } @@ -5217,10 +5769,10 @@ int make_schema_select(THD *thd, SELECT_LEX *sel, We have to make non const db_name & table_name because of lower_case_table_names */ - make_lex_string(thd, &db, INFORMATION_SCHEMA_NAME.str, - INFORMATION_SCHEMA_NAME.length, 0); - make_lex_string(thd, &table, schema_table->table_name, - strlen(schema_table->table_name), 0); + thd->make_lex_string(&db, INFORMATION_SCHEMA_NAME.str, + INFORMATION_SCHEMA_NAME.length, 0); + thd->make_lex_string(&table, schema_table->table_name, + strlen(schema_table->table_name), 0); if (schema_table->old_format(thd, schema_table) || /* Handle old syntax */ !sel->add_table_to_list(thd, new Table_ident(thd, db, table, 0), 0, 0, TL_READ)) @@ -5264,6 +5816,13 @@ bool get_schema_tables_result(JOIN *join, { bool is_subselect= (&lex->unit != lex->current_select->master_unit() && lex->current_select->master_unit()->item); + + + /* skip I_S optimizations specific to get_all_tables */ + if (thd->lex->describe && + (table_list->schema_table->fill_table != get_all_tables)) + continue; + /* If schema table is already processed and the statement is not a subselect then @@ -5343,479 +5902,537 @@ int fill_schema_files(THD *thd, TABLE_LIST *tables, COND *cond) ST_FIELD_INFO schema_fields_info[]= { - {"CATALOG_NAME", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"SCHEMA_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Database"}, - {"DEFAULT_CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0}, - {"DEFAULT_COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0}, - {"SQL_PATH", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"CATALOG_NAME", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"SCHEMA_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Database", + SKIP_OPEN_TABLE}, + {"DEFAULT_CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0, + SKIP_OPEN_TABLE}, + {"DEFAULT_COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"SQL_PATH", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO tables_fields_info[]= { - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name"}, - {"TABLE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"ENGINE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Engine"}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name", + SKIP_OPEN_TABLE}, + {"TABLE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, + {"ENGINE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Engine", OPEN_FRM_ONLY}, {"VERSION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Version"}, - {"ROW_FORMAT", 10, MYSQL_TYPE_STRING, 0, 1, "Row_format"}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Version", OPEN_FRM_ONLY}, + {"ROW_FORMAT", 10, MYSQL_TYPE_STRING, 0, 1, "Row_format", OPEN_FULL_TABLE}, {"TABLE_ROWS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Rows"}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Rows", OPEN_FULL_TABLE}, {"AVG_ROW_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Avg_row_length"}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Avg_row_length", OPEN_FULL_TABLE}, {"DATA_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_length"}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_length", OPEN_FULL_TABLE}, {"MAX_DATA_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Max_data_length"}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Max_data_length", OPEN_FULL_TABLE}, {"INDEX_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Index_length"}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Index_length", OPEN_FULL_TABLE}, {"DATA_FREE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_free"}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_free", OPEN_FULL_TABLE}, {"AUTO_INCREMENT", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Auto_increment"}, - {"CREATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Create_time"}, - {"UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Update_time"}, - {"CHECK_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Check_time"}, - {"TABLE_COLLATION", 64, MYSQL_TYPE_STRING, 0, 1, "Collation"}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Auto_increment", OPEN_FULL_TABLE}, + {"CREATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Create_time", OPEN_FULL_TABLE}, + {"UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Update_time", OPEN_FULL_TABLE}, + {"CHECK_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Check_time", OPEN_FULL_TABLE}, + {"TABLE_COLLATION", 64, MYSQL_TYPE_STRING, 0, 1, "Collation", OPEN_FRM_ONLY}, {"CHECKSUM", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Checksum"}, - {"CREATE_OPTIONS", 255, MYSQL_TYPE_STRING, 0, 1, "Create_options"}, - {"TABLE_COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, "Comment"}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Checksum", OPEN_FULL_TABLE}, + {"CREATE_OPTIONS", 255, MYSQL_TYPE_STRING, 0, 1, "Create_options", + OPEN_FRM_ONLY}, + {"TABLE_COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, "Comment", OPEN_FRM_ONLY}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO columns_fields_info[]= { - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Field"}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FRM_ONLY}, + {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, + {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, + {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Field", + OPEN_FRM_ONLY}, {"ORDINAL_POSITION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, - MY_I_S_UNSIGNED, 0}, + MY_I_S_UNSIGNED, 0, OPEN_FRM_ONLY}, {"COLUMN_DEFAULT", MAX_FIELD_VARCHARLENGTH, MYSQL_TYPE_STRING, 0, - 1, "Default"}, - {"IS_NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null"}, - {"DATA_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, + 1, "Default", OPEN_FRM_ONLY}, + {"IS_NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null", OPEN_FRM_ONLY}, + {"DATA_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, {"CHARACTER_MAXIMUM_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, - 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, + 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY}, {"CHARACTER_OCTET_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG, - 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, + 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY}, {"NUMERIC_PRECISION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, - 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, + 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY}, {"NUMERIC_SCALE", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG, - 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, - {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 1, 0}, - {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 1, "Collation"}, - {"COLUMN_TYPE", 65535, MYSQL_TYPE_STRING, 0, 0, "Type"}, - {"COLUMN_KEY", 3, MYSQL_TYPE_STRING, 0, 0, "Key"}, - {"EXTRA", 20, MYSQL_TYPE_STRING, 0, 0, "Extra"}, - {"PRIVILEGES", 80, MYSQL_TYPE_STRING, 0, 0, "Privileges"}, - {"COLUMN_COMMENT", 255, MYSQL_TYPE_STRING, 0, 0, "Comment"}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY}, + {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FRM_ONLY}, + {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 1, "Collation", OPEN_FRM_ONLY}, + {"COLUMN_TYPE", 65535, MYSQL_TYPE_STRING, 0, 0, "Type", OPEN_FRM_ONLY}, + {"COLUMN_KEY", 3, MYSQL_TYPE_STRING, 0, 0, "Key", OPEN_FRM_ONLY}, + {"EXTRA", 20, MYSQL_TYPE_STRING, 0, 0, "Extra", OPEN_FRM_ONLY}, + {"PRIVILEGES", 80, MYSQL_TYPE_STRING, 0, 0, "Privileges", OPEN_FRM_ONLY}, + {"COLUMN_COMMENT", 255, MYSQL_TYPE_STRING, 0, 0, "Comment", OPEN_FRM_ONLY}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO charsets_fields_info[]= { - {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Charset"}, - {"DEFAULT_COLLATE_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Default collation"}, - {"DESCRIPTION", 60, MYSQL_TYPE_STRING, 0, 0, "Description"}, - {"MAXLEN", 3, MYSQL_TYPE_LONGLONG, 0, 0, "Maxlen"}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Charset", + SKIP_OPEN_TABLE}, + {"DEFAULT_COLLATE_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Default collation", + SKIP_OPEN_TABLE}, + {"DESCRIPTION", 60, MYSQL_TYPE_STRING, 0, 0, "Description", + SKIP_OPEN_TABLE}, + {"MAXLEN", 3, MYSQL_TYPE_LONGLONG, 0, 0, "Maxlen", SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO collation_fields_info[]= { - {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Collation"}, - {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Charset"}, - {"ID", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Id"}, - {"IS_DEFAULT", 3, MYSQL_TYPE_STRING, 0, 0, "Default"}, - {"IS_COMPILED", 3, MYSQL_TYPE_STRING, 0, 0, "Compiled"}, - {"SORTLEN", 3, MYSQL_TYPE_LONGLONG, 0, 0, "Sortlen"}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Collation", SKIP_OPEN_TABLE}, + {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Charset", + SKIP_OPEN_TABLE}, + {"ID", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Id", + SKIP_OPEN_TABLE}, + {"IS_DEFAULT", 3, MYSQL_TYPE_STRING, 0, 0, "Default", SKIP_OPEN_TABLE}, + {"IS_COMPILED", 3, MYSQL_TYPE_STRING, 0, 0, "Compiled", SKIP_OPEN_TABLE}, + {"SORTLEN", 3, MYSQL_TYPE_LONGLONG, 0, 0, "Sortlen", SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO engines_fields_info[]= { - {"ENGINE", 64, MYSQL_TYPE_STRING, 0, 0, "Engine"}, - {"SUPPORT", 8, MYSQL_TYPE_STRING, 0, 0, "Support"}, - {"COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, "Comment"}, - {"TRANSACTIONS", 3, MYSQL_TYPE_STRING, 0, 0, "Transactions"}, - {"XA", 3, MYSQL_TYPE_STRING, 0, 0, "XA"}, - {"SAVEPOINTS", 3 ,MYSQL_TYPE_STRING, 0, 0, "Savepoints"}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"ENGINE", 64, MYSQL_TYPE_STRING, 0, 0, "Engine", SKIP_OPEN_TABLE}, + {"SUPPORT", 8, MYSQL_TYPE_STRING, 0, 0, "Support", SKIP_OPEN_TABLE}, + {"COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, "Comment", SKIP_OPEN_TABLE}, + {"TRANSACTIONS", 3, MYSQL_TYPE_STRING, 0, 0, "Transactions", SKIP_OPEN_TABLE}, + {"XA", 3, MYSQL_TYPE_STRING, 0, 0, "XA", SKIP_OPEN_TABLE}, + {"SAVEPOINTS", 3 ,MYSQL_TYPE_STRING, 0, 0, "Savepoints", SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO events_fields_info[]= { - {"EVENT_CATALOG", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"EVENT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Db"}, - {"EVENT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name"}, - {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, "Definer"}, - {"TIME_ZONE", 64, MYSQL_TYPE_STRING, 0, 0, "Time zone"}, - {"EVENT_BODY", 8, MYSQL_TYPE_STRING, 0, 0, 0}, - {"EVENT_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 0, 0}, - {"EVENT_TYPE", 9, MYSQL_TYPE_STRING, 0, 0, "Type"}, - {"EXECUTE_AT", 0, MYSQL_TYPE_DATETIME, 0, 1, "Execute at"}, - {"INTERVAL_VALUE", 256, MYSQL_TYPE_STRING, 0, 1, "Interval value"}, - {"INTERVAL_FIELD", 18, MYSQL_TYPE_STRING, 0, 1, "Interval field"}, - {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, 0}, - {"STARTS", 0, MYSQL_TYPE_DATETIME, 0, 1, "Starts"}, - {"ENDS", 0, MYSQL_TYPE_DATETIME, 0, 1, "Ends"}, - {"STATUS", 18, MYSQL_TYPE_STRING, 0, 0, "Status"}, - {"ON_COMPLETION", 12, MYSQL_TYPE_STRING, 0, 0, 0}, - {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 0, 0}, - {"LAST_ALTERED", 0, MYSQL_TYPE_DATETIME, 0, 0, 0}, - {"LAST_EXECUTED", 0, MYSQL_TYPE_DATETIME, 0, 1, 0}, - {"EVENT_COMMENT", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"ORIGINATOR", 10, MYSQL_TYPE_LONGLONG, 0, 0, "Originator"}, + {"EVENT_CATALOG", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"EVENT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Db", + SKIP_OPEN_TABLE}, + {"EVENT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name", + SKIP_OPEN_TABLE}, + {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, "Definer", SKIP_OPEN_TABLE}, + {"TIME_ZONE", 64, MYSQL_TYPE_STRING, 0, 0, "Time zone", SKIP_OPEN_TABLE}, + {"EVENT_BODY", 8, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"EVENT_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"EVENT_TYPE", 9, MYSQL_TYPE_STRING, 0, 0, "Type", SKIP_OPEN_TABLE}, + {"EXECUTE_AT", 0, MYSQL_TYPE_DATETIME, 0, 1, "Execute at", SKIP_OPEN_TABLE}, + {"INTERVAL_VALUE", 256, MYSQL_TYPE_STRING, 0, 1, "Interval value", + SKIP_OPEN_TABLE}, + {"INTERVAL_FIELD", 18, MYSQL_TYPE_STRING, 0, 1, "Interval field", + SKIP_OPEN_TABLE}, + {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"STARTS", 0, MYSQL_TYPE_DATETIME, 0, 1, "Starts", SKIP_OPEN_TABLE}, + {"ENDS", 0, MYSQL_TYPE_DATETIME, 0, 1, "Ends", SKIP_OPEN_TABLE}, + {"STATUS", 18, MYSQL_TYPE_STRING, 0, 0, "Status", SKIP_OPEN_TABLE}, + {"ON_COMPLETION", 12, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 0, 0, SKIP_OPEN_TABLE}, + {"LAST_ALTERED", 0, MYSQL_TYPE_DATETIME, 0, 0, 0, SKIP_OPEN_TABLE}, + {"LAST_EXECUTED", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, SKIP_OPEN_TABLE}, + {"EVENT_COMMENT", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"ORIGINATOR", 10, MYSQL_TYPE_LONGLONG, 0, 0, "Originator", SKIP_OPEN_TABLE}, {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, - "character_set_client"}, + "character_set_client", SKIP_OPEN_TABLE}, {"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, - "collation_connection"}, + "collation_connection", SKIP_OPEN_TABLE}, {"DATABASE_COLLATION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, - "Database Collation"}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + "Database Collation", SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO coll_charset_app_fields_info[]= { - {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0}, - {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO proc_fields_info[]= { - {"SPECIFIC_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"ROUTINE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"ROUTINE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Db"}, - {"ROUTINE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name"}, - {"ROUTINE_TYPE", 9, MYSQL_TYPE_STRING, 0, 0, "Type"}, - {"DTD_IDENTIFIER", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"ROUTINE_BODY", 8, MYSQL_TYPE_STRING, 0, 0, 0}, - {"ROUTINE_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 1, 0}, - {"EXTERNAL_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"EXTERNAL_LANGUAGE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"PARAMETER_STYLE", 8, MYSQL_TYPE_STRING, 0, 0, 0}, - {"IS_DETERMINISTIC", 3, MYSQL_TYPE_STRING, 0, 0, 0}, - {"SQL_DATA_ACCESS", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"SQL_PATH", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"SECURITY_TYPE", 7, MYSQL_TYPE_STRING, 0, 0, "Security_type"}, - {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 0, "Created"}, - {"LAST_ALTERED", 0, MYSQL_TYPE_DATETIME, 0, 0, "Modified"}, - {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, 0}, - {"ROUTINE_COMMENT", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Comment"}, - {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, "Definer"}, + {"SPECIFIC_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"ROUTINE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"ROUTINE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Db", + SKIP_OPEN_TABLE}, + {"ROUTINE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name", + SKIP_OPEN_TABLE}, + {"ROUTINE_TYPE", 9, MYSQL_TYPE_STRING, 0, 0, "Type", SKIP_OPEN_TABLE}, + {"DTD_IDENTIFIER", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"ROUTINE_BODY", 8, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"ROUTINE_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"EXTERNAL_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"EXTERNAL_LANGUAGE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, + SKIP_OPEN_TABLE}, + {"PARAMETER_STYLE", 8, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"IS_DETERMINISTIC", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"SQL_DATA_ACCESS", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, + SKIP_OPEN_TABLE}, + {"SQL_PATH", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"SECURITY_TYPE", 7, MYSQL_TYPE_STRING, 0, 0, "Security_type", + SKIP_OPEN_TABLE}, + {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 0, "Created", SKIP_OPEN_TABLE}, + {"LAST_ALTERED", 0, MYSQL_TYPE_DATETIME, 0, 0, "Modified", SKIP_OPEN_TABLE}, + {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"ROUTINE_COMMENT", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Comment", + SKIP_OPEN_TABLE}, + {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, "Definer", SKIP_OPEN_TABLE}, {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, - "character_set_client"}, + "character_set_client", SKIP_OPEN_TABLE}, {"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, - "collation_connection"}, + "collation_connection", SKIP_OPEN_TABLE}, {"DATABASE_COLLATION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, - "Database Collation"}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + "Database Collation", SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO stat_fields_info[]= { - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table"}, - {"NON_UNIQUE", 1, MYSQL_TYPE_LONGLONG, 0, 0, "Non_unique"}, - {"INDEX_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"INDEX_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Key_name"}, - {"SEQ_IN_INDEX", 2, MYSQL_TYPE_LONGLONG, 0, 0, "Seq_in_index"}, - {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Column_name"}, - {"COLLATION", 1, MYSQL_TYPE_STRING, 0, 1, "Collation"}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FRM_ONLY}, + {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, + {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table", OPEN_FRM_ONLY}, + {"NON_UNIQUE", 1, MYSQL_TYPE_LONGLONG, 0, 0, "Non_unique", OPEN_FRM_ONLY}, + {"INDEX_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, + {"INDEX_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Key_name", + OPEN_FRM_ONLY}, + {"SEQ_IN_INDEX", 2, MYSQL_TYPE_LONGLONG, 0, 0, "Seq_in_index", OPEN_FRM_ONLY}, + {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Column_name", + OPEN_FRM_ONLY}, + {"COLLATION", 1, MYSQL_TYPE_STRING, 0, 1, "Collation", OPEN_FRM_ONLY}, {"CARDINALITY", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 1, - "Cardinality"}, - {"SUB_PART", 3, MYSQL_TYPE_LONGLONG, 0, 1, "Sub_part"}, - {"PACKED", 10, MYSQL_TYPE_STRING, 0, 1, "Packed"}, - {"NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null"}, - {"INDEX_TYPE", 16, MYSQL_TYPE_STRING, 0, 0, "Index_type"}, - {"COMMENT", 16, MYSQL_TYPE_STRING, 0, 1, "Comment"}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + "Cardinality", OPEN_FULL_TABLE}, + {"SUB_PART", 3, MYSQL_TYPE_LONGLONG, 0, 1, "Sub_part", OPEN_FRM_ONLY}, + {"PACKED", 10, MYSQL_TYPE_STRING, 0, 1, "Packed", OPEN_FRM_ONLY}, + {"NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null", OPEN_FRM_ONLY}, + {"INDEX_TYPE", 16, MYSQL_TYPE_STRING, 0, 0, "Index_type", OPEN_FULL_TABLE}, + {"COMMENT", 16, MYSQL_TYPE_STRING, 0, 1, "Comment", OPEN_FRM_ONLY}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO view_fields_info[]= { - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"VIEW_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 0, 0}, - {"CHECK_OPTION", 8, MYSQL_TYPE_STRING, 0, 0, 0}, - {"IS_UPDATABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0}, - {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, 0}, - {"SECURITY_TYPE", 7, MYSQL_TYPE_STRING, 0, 0, 0}, - {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0}, - {"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FRM_ONLY}, + {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, + {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, + {"VIEW_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"CHECK_OPTION", 8, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"IS_UPDATABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"SECURITY_TYPE", 7, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0, + OPEN_FULL_TABLE}, + {"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0, + OPEN_FULL_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO user_privileges_fields_info[]= { - {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO schema_privileges_fields_info[]= { - {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO table_privileges_fields_info[]= { - {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO column_privileges_fields_info[]= { - {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO table_constraints_fields_info[]= { - {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"CONSTRAINT_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, + OPEN_FULL_TABLE}, + {"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, + OPEN_FULL_TABLE}, + {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"CONSTRAINT_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, + OPEN_FULL_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO key_column_usage_fields_info[]= { - {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"ORDINAL_POSITION", 10 ,MYSQL_TYPE_LONGLONG, 0, 0, 0}, - {"POSITION_IN_UNIQUE_CONSTRAINT", 10 ,MYSQL_TYPE_LONGLONG, 0, 1, 0}, - {"REFERENCED_TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"REFERENCED_TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"REFERENCED_COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, + OPEN_FULL_TABLE}, + {"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, + OPEN_FULL_TABLE}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"ORDINAL_POSITION", 10 ,MYSQL_TYPE_LONGLONG, 0, 0, 0, OPEN_FULL_TABLE}, + {"POSITION_IN_UNIQUE_CONSTRAINT", 10 ,MYSQL_TYPE_LONGLONG, 0, 1, 0, + OPEN_FULL_TABLE}, + {"REFERENCED_TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, + OPEN_FULL_TABLE}, + {"REFERENCED_TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, + OPEN_FULL_TABLE}, + {"REFERENCED_COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, + OPEN_FULL_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO table_names_fields_info[]= { - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"TABLE_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Tables_in_"}, - {"TABLE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_type"}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"TABLE_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Tables_in_", + SKIP_OPEN_TABLE}, + {"TABLE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_type", + OPEN_FRM_ONLY}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO open_tables_fields_info[]= { - {"Database", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Database"}, - {"Table",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table"}, - {"In_use", 1, MYSQL_TYPE_LONGLONG, 0, 0, "In_use"}, - {"Name_locked", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Name_locked"}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"Database", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Database", + SKIP_OPEN_TABLE}, + {"Table",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table", SKIP_OPEN_TABLE}, + {"In_use", 1, MYSQL_TYPE_LONGLONG, 0, 0, "In_use", SKIP_OPEN_TABLE}, + {"Name_locked", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Name_locked", SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO triggers_fields_info[]= { - {"TRIGGER_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"TRIGGER_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TRIGGER_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Trigger"}, - {"EVENT_MANIPULATION", 6, MYSQL_TYPE_STRING, 0, 0, "Event"}, - {"EVENT_OBJECT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"EVENT_OBJECT_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"EVENT_OBJECT_TABLE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table"}, - {"ACTION_ORDER", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0}, - {"ACTION_CONDITION", 65535, MYSQL_TYPE_STRING, 0, 1, 0}, - {"ACTION_STATEMENT", 65535, MYSQL_TYPE_STRING, 0, 0, "Statement"}, - {"ACTION_ORIENTATION", 9, MYSQL_TYPE_STRING, 0, 0, 0}, - {"ACTION_TIMING", 6, MYSQL_TYPE_STRING, 0, 0, "Timing"}, - {"ACTION_REFERENCE_OLD_TABLE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"ACTION_REFERENCE_NEW_TABLE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"ACTION_REFERENCE_OLD_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0}, - {"ACTION_REFERENCE_NEW_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0}, - {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 1, "Created"}, - {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, "sql_mode"}, - {"DEFINER", 65535, MYSQL_TYPE_STRING, 0, 0, "Definer"}, + {"TRIGGER_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"TRIGGER_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"TRIGGER_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Trigger", + OPEN_FULL_TABLE}, + {"EVENT_MANIPULATION", 6, MYSQL_TYPE_STRING, 0, 0, "Event", OPEN_FULL_TABLE}, + {"EVENT_OBJECT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, + OPEN_FULL_TABLE}, + {"EVENT_OBJECT_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, + OPEN_FULL_TABLE}, + {"EVENT_OBJECT_TABLE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table", + OPEN_FULL_TABLE}, + {"ACTION_ORDER", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0, OPEN_FULL_TABLE}, + {"ACTION_CONDITION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"ACTION_STATEMENT", 65535, MYSQL_TYPE_STRING, 0, 0, "Statement", + OPEN_FULL_TABLE}, + {"ACTION_ORIENTATION", 9, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"ACTION_TIMING", 6, MYSQL_TYPE_STRING, 0, 0, "Timing", OPEN_FULL_TABLE}, + {"ACTION_REFERENCE_OLD_TABLE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, + OPEN_FULL_TABLE}, + {"ACTION_REFERENCE_NEW_TABLE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, + OPEN_FULL_TABLE}, + {"ACTION_REFERENCE_OLD_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"ACTION_REFERENCE_NEW_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 1, "Created", OPEN_FULL_TABLE}, + {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, "sql_mode", OPEN_FULL_TABLE}, + {"DEFINER", 65535, MYSQL_TYPE_STRING, 0, 0, "Definer", OPEN_FULL_TABLE}, {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, - "character_set_client"}, + "character_set_client", OPEN_FULL_TABLE}, {"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, - "collation_connection"}, + "collation_connection", OPEN_FULL_TABLE}, {"DATABASE_COLLATION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, - "Database Collation"}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + "Database Collation", OPEN_FULL_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO partitions_fields_info[]= { - {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"TABLE_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"PARTITION_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"SUBPARTITION_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, + {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"TABLE_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"PARTITION_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"SUBPARTITION_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, + OPEN_FULL_TABLE}, {"PARTITION_ORDINAL_POSITION", 21 , MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE}, {"SUBPARTITION_ORDINAL_POSITION", 21 , MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, - {"PARTITION_METHOD", 12, MYSQL_TYPE_STRING, 0, 1, 0}, - {"SUBPARTITION_METHOD", 12, MYSQL_TYPE_STRING, 0, 1, 0}, - {"PARTITION_EXPRESSION", 65535, MYSQL_TYPE_STRING, 0, 1, 0}, - {"SUBPARTITION_EXPRESSION", 65535, MYSQL_TYPE_STRING, 0, 1, 0}, - {"PARTITION_DESCRIPTION", 65535, MYSQL_TYPE_STRING, 0, 1, 0}, - {"TABLE_ROWS", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0}, - {"AVG_ROW_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0}, - {"DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE}, + {"PARTITION_METHOD", 12, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"SUBPARTITION_METHOD", 12, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"PARTITION_EXPRESSION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"SUBPARTITION_EXPRESSION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, + OPEN_FULL_TABLE}, + {"PARTITION_DESCRIPTION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"TABLE_ROWS", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0, + OPEN_FULL_TABLE}, + {"AVG_ROW_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0, + OPEN_FULL_TABLE}, + {"DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0, + OPEN_FULL_TABLE}, {"MAX_DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, - {"INDEX_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0}, - {"DATA_FREE", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0}, - {"CREATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0}, - {"UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0}, - {"CHECK_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE}, + {"INDEX_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0, + OPEN_FULL_TABLE}, + {"DATA_FREE", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0, + OPEN_FULL_TABLE}, + {"CREATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, OPEN_FULL_TABLE}, + {"UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, OPEN_FULL_TABLE}, + {"CHECK_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, OPEN_FULL_TABLE}, {"CHECKSUM", 21 , MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, - {"PARTITION_COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, 0}, - {"NODEGROUP", 12 , MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLESPACE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE}, + {"PARTITION_COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"NODEGROUP", 12 , MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"TABLESPACE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, + OPEN_FULL_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO variables_fields_info[]= { - {"VARIABLE_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Variable_name"}, - {"VARIABLE_VALUE", 20480, MYSQL_TYPE_STRING, 0, 1, "Value"}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"VARIABLE_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Variable_name", + SKIP_OPEN_TABLE}, + {"VARIABLE_VALUE", 20480, MYSQL_TYPE_STRING, 0, 1, "Value", SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO processlist_fields_info[]= { - {"ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Id"}, - {"USER", 16, MYSQL_TYPE_STRING, 0, 0, "User"}, - {"HOST", LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Host"}, - {"DB", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Db"}, - {"COMMAND", 16, MYSQL_TYPE_STRING, 0, 0, "Command"}, - {"TIME", 7, MYSQL_TYPE_LONGLONG, 0, 0, "Time"}, - {"STATE", 64, MYSQL_TYPE_STRING, 0, 1, "State"}, - {"INFO", PROCESS_LIST_INFO_WIDTH, MYSQL_TYPE_STRING, 0, 1, "Info"}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Id", SKIP_OPEN_TABLE}, + {"USER", 16, MYSQL_TYPE_STRING, 0, 0, "User", SKIP_OPEN_TABLE}, + {"HOST", LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Host", + SKIP_OPEN_TABLE}, + {"DB", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Db", SKIP_OPEN_TABLE}, + {"COMMAND", 16, MYSQL_TYPE_STRING, 0, 0, "Command", SKIP_OPEN_TABLE}, + {"TIME", 7, MYSQL_TYPE_LONGLONG, 0, 0, "Time", SKIP_OPEN_TABLE}, + {"STATE", 64, MYSQL_TYPE_STRING, 0, 1, "State", SKIP_OPEN_TABLE}, + {"INFO", PROCESS_LIST_INFO_WIDTH, MYSQL_TYPE_STRING, 0, 1, "Info", + SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO plugin_fields_info[]= { - {"PLUGIN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name"}, - {"PLUGIN_VERSION", 20, MYSQL_TYPE_STRING, 0, 0, 0}, - {"PLUGIN_STATUS", 10, MYSQL_TYPE_STRING, 0, 0, "Status"}, - {"PLUGIN_TYPE", 80, MYSQL_TYPE_STRING, 0, 0, "Type"}, - {"PLUGIN_TYPE_VERSION", 20, MYSQL_TYPE_STRING, 0, 0, 0}, - {"PLUGIN_LIBRARY", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Library"}, - {"PLUGIN_LIBRARY_VERSION", 20, MYSQL_TYPE_STRING, 0, 1, 0}, - {"PLUGIN_AUTHOR", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"PLUGIN_DESCRIPTION", 65535, MYSQL_TYPE_STRING, 0, 1, 0}, - {"PLUGIN_LICENSE", 80, MYSQL_TYPE_STRING, 0, 1, "License"}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"PLUGIN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name", + SKIP_OPEN_TABLE}, + {"PLUGIN_VERSION", 20, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"PLUGIN_STATUS", 10, MYSQL_TYPE_STRING, 0, 0, "Status", SKIP_OPEN_TABLE}, + {"PLUGIN_TYPE", 80, MYSQL_TYPE_STRING, 0, 0, "Type", SKIP_OPEN_TABLE}, + {"PLUGIN_TYPE_VERSION", 20, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"PLUGIN_LIBRARY", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Library", + SKIP_OPEN_TABLE}, + {"PLUGIN_LIBRARY_VERSION", 20, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"PLUGIN_AUTHOR", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"PLUGIN_DESCRIPTION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"PLUGIN_LICENSE", 80, MYSQL_TYPE_STRING, 0, 1, "License", SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; ST_FIELD_INFO files_fields_info[]= { - {"FILE_ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0}, - {"FILE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"FILE_TYPE", 20, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLESPACE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"TABLE_CATALOG", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"LOGFILE_GROUP_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"LOGFILE_GROUP_NUMBER", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0}, - {"ENGINE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"FULLTEXT_KEYS", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"DELETED_ROWS", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0}, - {"UPDATE_COUNT", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0}, - {"FREE_EXTENTS", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0}, - {"TOTAL_EXTENTS", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0}, - {"EXTENT_SIZE", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0}, + {"FILE_ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0, SKIP_OPEN_TABLE}, + {"FILE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"FILE_TYPE", 20, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"TABLESPACE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, + SKIP_OPEN_TABLE}, + {"TABLE_CATALOG", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"LOGFILE_GROUP_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, + SKIP_OPEN_TABLE}, + {"LOGFILE_GROUP_NUMBER", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE}, + {"ENGINE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"FULLTEXT_KEYS", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"DELETED_ROWS", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE}, + {"UPDATE_COUNT", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE}, + {"FREE_EXTENTS", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE}, + {"TOTAL_EXTENTS", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE}, + {"EXTENT_SIZE", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0, SKIP_OPEN_TABLE}, {"INITIAL_SIZE", 21, MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE}, {"MAXIMUM_SIZE", 21, MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE}, {"AUTOEXTEND_SIZE", 21, MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, - {"CREATION_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0}, - {"LAST_UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0}, - {"LAST_ACCESS_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0}, - {"RECOVER_TIME", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0}, - {"TRANSACTION_COUNTER", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE}, + {"CREATION_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, SKIP_OPEN_TABLE}, + {"LAST_UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, SKIP_OPEN_TABLE}, + {"LAST_ACCESS_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, SKIP_OPEN_TABLE}, + {"RECOVER_TIME", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE}, + {"TRANSACTION_COUNTER", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE}, {"VERSION", 21 , MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Version"}, - {"ROW_FORMAT", 10, MYSQL_TYPE_STRING, 0, 1, "Row_format"}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Version", SKIP_OPEN_TABLE}, + {"ROW_FORMAT", 10, MYSQL_TYPE_STRING, 0, 1, "Row_format", SKIP_OPEN_TABLE}, {"TABLE_ROWS", 21 , MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Rows"}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Rows", SKIP_OPEN_TABLE}, {"AVG_ROW_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Avg_row_length"}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Avg_row_length", SKIP_OPEN_TABLE}, {"DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_length"}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_length", SKIP_OPEN_TABLE}, {"MAX_DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Max_data_length"}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Max_data_length", SKIP_OPEN_TABLE}, {"INDEX_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Index_length"}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Index_length", SKIP_OPEN_TABLE}, {"DATA_FREE", 21 , MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_free"}, - {"CREATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Create_time"}, - {"UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Update_time"}, - {"CHECK_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Check_time"}, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_free", SKIP_OPEN_TABLE}, + {"CREATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Create_time", SKIP_OPEN_TABLE}, + {"UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Update_time", SKIP_OPEN_TABLE}, + {"CHECK_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Check_time", SKIP_OPEN_TABLE}, {"CHECKSUM", 21 , MYSQL_TYPE_LONGLONG, 0, - (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Checksum"}, - {"STATUS", 20, MYSQL_TYPE_STRING, 0, 0, 0}, - {"EXTRA", 255, MYSQL_TYPE_STRING, 0, 1, 0}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Checksum", SKIP_OPEN_TABLE}, + {"STATUS", 20, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"EXTRA", 255, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; void init_fill_schema_files_row(TABLE* table) @@ -5830,18 +6447,24 @@ void init_fill_schema_files_row(TABLE* table) ST_FIELD_INFO referential_constraints_fields_info[]= { - {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"UNIQUE_CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"UNIQUE_CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"UNIQUE_CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"MATCH_OPTION", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"UPDATE_RULE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"DELETE_RULE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"REFERENCED_TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, + {"CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, + OPEN_FULL_TABLE}, + {"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, + OPEN_FULL_TABLE}, + {"UNIQUE_CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, + OPEN_FULL_TABLE}, + {"UNIQUE_CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, + OPEN_FULL_TABLE}, + {"UNIQUE_CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, + OPEN_FULL_TABLE}, + {"MATCH_OPTION", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"UPDATE_RULE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"DELETE_RULE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"REFERENCED_TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, + OPEN_FULL_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; @@ -5855,69 +6478,75 @@ ST_FIELD_INFO referential_constraints_fields_info[]= ST_SCHEMA_TABLE schema_tables[]= { {"CHARACTER_SETS", charsets_fields_info, create_schema_table, - fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0}, + fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0, 0}, {"COLLATIONS", collation_fields_info, create_schema_table, - fill_schema_collation, make_old_format, 0, -1, -1, 0}, + fill_schema_collation, make_old_format, 0, -1, -1, 0, 0}, {"COLLATION_CHARACTER_SET_APPLICABILITY", coll_charset_app_fields_info, - create_schema_table, fill_schema_coll_charset_app, 0, 0, -1, -1, 0}, + create_schema_table, fill_schema_coll_charset_app, 0, 0, -1, -1, 0, 0}, {"COLUMNS", columns_fields_info, create_schema_table, - get_all_tables, make_columns_old_format, get_schema_column_record, 1, 2, 0}, + get_all_tables, make_columns_old_format, get_schema_column_record, 1, 2, 0, + OPTIMIZE_I_S_TABLE|OPEN_VIEW_FULL}, {"COLUMN_PRIVILEGES", column_privileges_fields_info, create_schema_table, - fill_schema_column_privileges, 0, 0, -1, -1, 0}, + fill_schema_column_privileges, 0, 0, -1, -1, 0, 0}, {"ENGINES", engines_fields_info, create_schema_table, - fill_schema_engines, make_old_format, 0, -1, -1, 0}, + fill_schema_engines, make_old_format, 0, -1, -1, 0, 0}, {"EVENTS", events_fields_info, create_schema_table, - Events::fill_schema_events, make_old_format, 0, -1, -1, 0}, + Events::fill_schema_events, make_old_format, 0, -1, -1, 0, 0}, {"FILES", files_fields_info, create_schema_table, - fill_schema_files, 0, 0, -1, -1, 0}, + fill_schema_files, 0, 0, -1, -1, 0, 0}, {"GLOBAL_STATUS", variables_fields_info, create_schema_table, - fill_status, make_old_format, 0, -1, -1, 0}, + fill_status, make_old_format, 0, -1, -1, 0, 0}, {"GLOBAL_VARIABLES", variables_fields_info, create_schema_table, - fill_variables, make_old_format, 0, -1, -1, 0}, + fill_variables, make_old_format, 0, -1, -1, 0, 0}, {"KEY_COLUMN_USAGE", key_column_usage_fields_info, create_schema_table, - get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0}, + get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0, + OPEN_TABLE_ONLY}, {"OPEN_TABLES", open_tables_fields_info, create_schema_table, - fill_open_tables, make_old_format, 0, -1, -1, 1}, + fill_open_tables, make_old_format, 0, -1, -1, 1, 0}, {"PARTITIONS", partitions_fields_info, create_schema_table, - get_all_tables, 0, get_schema_partitions_record, 1, 2, 0}, + get_all_tables, 0, get_schema_partitions_record, 1, 2, 0, OPEN_TABLE_ONLY}, {"PLUGINS", plugin_fields_info, create_schema_table, - fill_plugins, make_old_format, 0, -1, -1, 0}, + fill_plugins, make_old_format, 0, -1, -1, 0, 0}, {"PROCESSLIST", processlist_fields_info, create_schema_table, - fill_schema_processlist, make_old_format, 0, -1, -1, 0}, + fill_schema_processlist, make_old_format, 0, -1, -1, 0, 0}, {"REFERENTIAL_CONSTRAINTS", referential_constraints_fields_info, create_schema_table, get_all_tables, 0, get_referential_constraints_record, - 1, 9, 0}, + 1, 9, 0, OPEN_TABLE_ONLY}, {"ROUTINES", proc_fields_info, create_schema_table, - fill_schema_proc, make_proc_old_format, 0, -1, -1, 0}, + fill_schema_proc, make_proc_old_format, 0, -1, -1, 0, 0}, {"SCHEMATA", schema_fields_info, create_schema_table, - fill_schema_shemata, make_schemata_old_format, 0, 1, -1, 0}, + fill_schema_schemata, make_schemata_old_format, 0, 1, -1, 0, 0}, {"SCHEMA_PRIVILEGES", schema_privileges_fields_info, create_schema_table, - fill_schema_schema_privileges, 0, 0, -1, -1, 0}, + fill_schema_schema_privileges, 0, 0, -1, -1, 0, 0}, {"SESSION_STATUS", variables_fields_info, create_schema_table, - fill_status, make_old_format, 0, -1, -1, 0}, + fill_status, make_old_format, 0, -1, -1, 0, 0}, {"SESSION_VARIABLES", variables_fields_info, create_schema_table, - fill_variables, make_old_format, 0, -1, -1, 0}, + fill_variables, make_old_format, 0, -1, -1, 0, 0}, {"STATISTICS", stat_fields_info, create_schema_table, - get_all_tables, make_old_format, get_schema_stat_record, 1, 2, 0}, + get_all_tables, make_old_format, get_schema_stat_record, 1, 2, 0, + OPEN_TABLE_ONLY|OPTIMIZE_I_S_TABLE}, {"STATUS", variables_fields_info, create_schema_table, fill_status, - make_old_format, 0, -1, -1, 1}, + make_old_format, 0, -1, -1, 1, 0}, {"TABLES", tables_fields_info, create_schema_table, - get_all_tables, make_old_format, get_schema_tables_record, 1, 2, 0}, + get_all_tables, make_old_format, get_schema_tables_record, 1, 2, 0, + OPTIMIZE_I_S_TABLE}, {"TABLE_CONSTRAINTS", table_constraints_fields_info, create_schema_table, - get_all_tables, 0, get_schema_constraints_record, 3, 4, 0}, + get_all_tables, 0, get_schema_constraints_record, 3, 4, 0, OPEN_TABLE_ONLY}, {"TABLE_NAMES", table_names_fields_info, create_schema_table, - get_all_tables, make_table_names_old_format, 0, 1, 2, 1}, + get_all_tables, make_table_names_old_format, 0, 1, 2, 1, 0}, {"TABLE_PRIVILEGES", table_privileges_fields_info, create_schema_table, - fill_schema_table_privileges, 0, 0, -1, -1, 0}, + fill_schema_table_privileges, 0, 0, -1, -1, 0, 0}, {"TRIGGERS", triggers_fields_info, create_schema_table, - get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0}, + get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0, + OPEN_TABLE_ONLY}, {"USER_PRIVILEGES", user_privileges_fields_info, create_schema_table, - fill_schema_user_privileges, 0, 0, -1, -1, 0}, + fill_schema_user_privileges, 0, 0, -1, -1, 0, 0}, {"VARIABLES", variables_fields_info, create_schema_table, fill_variables, - make_old_format, 0, -1, -1, 1}, + make_old_format, 0, -1, -1, 1, 0}, {"VIEWS", view_fields_info, create_schema_table, - get_all_tables, 0, get_schema_views_record, 1, 2, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0} + get_all_tables, 0, get_schema_views_record, 1, 2, 0, + OPEN_VIEW_ONLY|OPTIMIZE_I_S_TABLE}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; @@ -5943,12 +6572,17 @@ int initialize_schema_table(st_plugin_int *plugin) schema_table->idx_field1= -1, schema_table->idx_field2= -1; + /* Make the name available to the init() function. */ + schema_table->table_name= plugin->name.str; + if (plugin->plugin->init(schema_table)) { sql_print_error("Plugin '%s' init function returned error.", plugin->name.str); goto err; } + + /* Make sure the plugin name is not set inside the init() function. */ schema_table->table_name= plugin->name.str; } @@ -6006,6 +6640,8 @@ static bool show_create_trigger_impl(THD *thd, LEX_STRING trg_connection_cl_name; LEX_STRING trg_db_cl_name; + CHARSET_INFO *trg_client_cs; + /* TODO: Check privileges here. This functionality will be added by implementation of the following WL items: @@ -6031,6 +6667,11 @@ static bool show_create_trigger_impl(THD *thd, trg_sql_mode, &trg_sql_mode_str); + /* Resolve trigger client character set. */ + + if (resolve_charset(trg_client_cs_name.str, NULL, &trg_client_cs)) + return TRUE; + /* Send header. */ fields.push_back(new Item_empty_string("Trigger", NAME_LEN)); @@ -6077,7 +6718,7 @@ static bool show_create_trigger_impl(THD *thd, p->store(trg_sql_original_stmt.str, trg_sql_original_stmt.length, - &my_charset_bin); + trg_client_cs); p->store(trg_client_cs_name.str, trg_client_cs_name.length, diff --git a/sql/sql_show.h b/sql/sql_show.h index d5c3f3bf675..d63217584b2 100644 --- a/sql/sql_show.h +++ b/sql/sql_show.h @@ -20,9 +20,8 @@ class String; class THD; struct st_ha_create_information; -struct st_table_list; typedef st_ha_create_information HA_CREATE_INFO; -typedef st_table_list TABLE_LIST; +struct TABLE_LIST; enum find_files_result { FIND_FILES_OK, @@ -30,7 +29,7 @@ enum find_files_result { FIND_FILES_DIR }; -find_files_result find_files(THD *thd, List<char> *files, const char *db, +find_files_result find_files(THD *thd, List<LEX_STRING> *files, const char *db, const char *path, const char *wild, bool dir); int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, diff --git a/sql/sql_sort.h b/sql/sql_sort.h index 1572f6304e1..f54b085eeda 100644 --- a/sql/sql_sort.h +++ b/sql/sql_sort.h @@ -44,6 +44,11 @@ typedef struct st_sort_addon_field uint8 null_bit; /* Null bit mask for the field */ } SORT_ADDON_FIELD; +struct BUFFPEK_COMPARE_CONTEXT +{ + qsort_cmp2 key_compare; + void *key_compare_arg; +}; typedef struct st_sort_param { uint rec_length; /* Length of sorted records */ @@ -60,6 +65,9 @@ typedef struct st_sort_param { uchar *unique_buff; bool not_killable; char* tmp_buffer; + /* The fields below are used only by Unique class */ + qsort2_cmp compare; + BUFFPEK_COMPARE_CONTEXT cmp_context; } SORTPARAM; diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 6c7dea6bf22..a8eb7360339 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -795,10 +795,8 @@ copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs, const uchar *from_end= (const uchar*) from+from_length; char *to_start= to; uchar *to_end= (uchar*) to+to_length; - int (*mb_wc)(struct charset_info_st *, my_wc_t *, const uchar *, - const uchar *) = from_cs->cset->mb_wc; - int (*wc_mb)(struct charset_info_st *, my_wc_t, uchar *s, uchar *e)= - to_cs->cset->wc_mb; + my_charset_conv_mb_wc mb_wc= from_cs->cset->mb_wc; + my_charset_conv_wc_mb wc_mb= to_cs->cset->wc_mb; uint error_count= 0; while (1) @@ -940,10 +938,8 @@ well_formed_copy_nchars(CHARSET_INFO *to_cs, { int cnvres; my_wc_t wc; - int (*mb_wc)(struct charset_info_st *, my_wc_t *, - const uchar *, const uchar *)= from_cs->cset->mb_wc; - int (*wc_mb)(struct charset_info_st *, my_wc_t, - uchar *s, uchar *e)= to_cs->cset->wc_mb; + my_charset_conv_mb_wc mb_wc= from_cs->cset->mb_wc; + my_charset_conv_wc_mb wc_mb= to_cs->cset->wc_mb; const uchar *from_end= (const uchar*) from + from_length; uchar *to_end= (uchar*) to + to_length; char *to_start= to; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 98b64551de7..81eba25b1fc 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -54,6 +54,20 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, HA_CREATE_INFO *create_info, Alter_info *alter_info); +#ifndef DBUG_OFF + +/* Wait until we get a 'mysql_kill' signal */ + +static void wait_for_kill_signal(THD *thd) +{ + while (thd->killed == 0) + sleep(1); + // Reset signal and continue as if nothing happend + thd->killed= THD::NOT_KILLED; +} +#endif + + /* Translate a file name to a table name (WL #1324). @@ -143,6 +157,7 @@ uint tablename_to_filename(const char *from, char *to, uint to_length) SYNOPSIS build_table_filename() buff Where to write result in my_charset_filename. + This may be the same as table_name. bufflen buff size db Database name in system_charset_info. table_name Table name in system_charset_info. @@ -172,10 +187,11 @@ uint tablename_to_filename(const char *from, char *to, uint to_length) uint build_table_filename(char *buff, size_t bufflen, const char *db, const char *table_name, const char *ext, uint flags) { - uint length; char dbbuff[FN_REFLEN]; char tbbuff[FN_REFLEN]; DBUG_ENTER("build_table_filename"); + DBUG_PRINT("enter", ("db: '%s' table_name: '%s' ext: '%s' flags: %x", + db, table_name, ext, flags)); if (flags & FN_IS_TMP) // FN_FROM_IS_TMP | FN_TO_IS_TMP strnmov(tbbuff, table_name, sizeof(tbbuff)); @@ -183,10 +199,18 @@ uint build_table_filename(char *buff, size_t bufflen, const char *db, VOID(tablename_to_filename(table_name, tbbuff, sizeof(tbbuff))); VOID(tablename_to_filename(db, dbbuff, sizeof(dbbuff))); - length= strxnmov(buff, bufflen, mysql_data_home, FN_ROOTDIR, dbbuff, - FN_ROOTDIR, tbbuff, ext, NullS) - buff; + + char *end = buff + bufflen; + /* Don't add FN_ROOTDIR if mysql_data_home already includes it */ + char *pos = strnmov(buff, mysql_data_home, bufflen); + int rootdir_len= strlen(FN_ROOTDIR); + if (pos - rootdir_len >= buff && + memcmp(pos - rootdir_len, FN_ROOTDIR, rootdir_len) != 0) + pos= strnmov(pos, FN_ROOTDIR, end - pos); + pos= strxnmov(pos, end - pos, dbbuff, FN_ROOTDIR, tbbuff, ext, NullS); + DBUG_PRINT("exit", ("buff: '%s'", buff)); - DBUG_RETURN(length); + DBUG_RETURN(pos - buff); } @@ -1430,19 +1454,8 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, LOCK_open during wait_if_global_read_lock(), other threads could not close their tables. This would make a pretty deadlock. */ - thd->mysys_var->current_mutex= &LOCK_open; - thd->mysys_var->current_cond= &COND_refresh; - VOID(pthread_mutex_lock(&LOCK_open)); - error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0); - pthread_mutex_unlock(&LOCK_open); - - pthread_mutex_lock(&thd->mysys_var->mutex); - thd->mysys_var->current_mutex= 0; - thd->mysys_var->current_cond= 0; - pthread_mutex_unlock(&thd->mysys_var->mutex); - if (need_start_waiters) start_waiting_global_read_lock(thd); @@ -1452,49 +1465,6 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, DBUG_RETURN(FALSE); } - -/* - delete (drop) tables. - - SYNOPSIS - mysql_rm_table_part2_with_lock() - thd Thread handle - tables List of tables to delete - if_exists If 1, don't give error if one table doesn't exists - dont_log_query Don't write query to log files. This will also not - generate warnings if the handler files doesn't exists - - NOTES - Works like documented in mysql_rm_table(), but don't check - global_read_lock and don't send_ok packet to server. - - RETURN - 0 ok - 1 error -*/ - -int mysql_rm_table_part2_with_lock(THD *thd, - TABLE_LIST *tables, bool if_exists, - bool drop_temporary, bool dont_log_query) -{ - int error; - thd->mysys_var->current_mutex= &LOCK_open; - thd->mysys_var->current_cond= &COND_refresh; - VOID(pthread_mutex_lock(&LOCK_open)); - - error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 1, - dont_log_query); - - pthread_mutex_unlock(&LOCK_open); - - pthread_mutex_lock(&thd->mysys_var->mutex); - thd->mysys_var->current_mutex= 0; - thd->mysys_var->current_cond= 0; - pthread_mutex_unlock(&thd->mysys_var->mutex); - return error; -} - - /* Execute the drop of a normal or temporary table @@ -1541,7 +1511,6 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, LINT_INIT(alias); LINT_INIT(path_length); - safe_mutex_assert_owner(&LOCK_open); if (thd->current_stmt_binlog_row_based && !dont_log_query) { @@ -1551,6 +1520,9 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, else built_query.append("DROP TABLE "); } + + pthread_mutex_lock(&LOCK_open); + /* If we have the table in the definition cache, we don't have to check the .frm file to find if the table is a normal table (not view) and what @@ -1565,17 +1537,21 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, table->db_type= share->db_type(); /* Disable drop of enabled log tables */ - if (share && share->log_table && + if (share && (share->table_category == TABLE_CATEGORY_PERFORMANCE) && 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"); + pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(1); } } - if (!drop_temporary && lock_table_names(thd, tables)) + if (!drop_temporary && lock_table_names_exclusively(thd, tables)) + { + pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(1); + } /* Don't give warnings for not found errors, as we already generate notes */ thd->no_warnings_for_error= 1; @@ -1586,7 +1562,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, handlerton *table_type; enum legacy_db_type frm_db_type; - mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL, TRUE); + mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL, 1); if (!close_temporary_table(thd, table)) { tmp_table_deleted=1; @@ -1635,8 +1611,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, if (thd->killed) { - thd->no_warnings_for_error= 0; - DBUG_RETURN(-1); + error= -1; + goto err_with_placeholders; } alias= (lower_case_table_names == 2) ? table->alias : table->table_name; /* remove .frm file and engine files */ @@ -1699,7 +1675,12 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, wrong_tables.append(String(table->table_name,system_charset_info)); } } - thd->tmp_table_used= tmp_table_deleted; + /* + It's safe to unlock LOCK_open: we have an exclusive lock + on the table name. + */ + pthread_mutex_unlock(&LOCK_open); + thd->thread_specific_used|= tmp_table_deleted; error= 0; if (wrong_tables.length()) { @@ -1758,9 +1739,10 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, */ } } - - if (!drop_temporary) - unlock_table_names(thd, tables, (TABLE_LIST*) 0); + pthread_mutex_lock(&LOCK_open); +err_with_placeholders: + unlock_table_names(thd, tables, (TABLE_LIST*) 0); + pthread_mutex_unlock(&LOCK_open); thd->no_warnings_for_error= 0; DBUG_RETURN(error); } @@ -2831,6 +2813,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, length); push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_TOO_LONG_KEY, warn_buff); + /* Align key length to multibyte char boundary */ + length-= length % sql_field->charset->mbmaxlen; } else { @@ -2861,8 +2845,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (length > file->max_key_part_length() && key->type != Key::FULLTEXT) { length= file->max_key_part_length(); - /* Align key length to multibyte char boundary */ - length-= length % sql_field->charset->mbmaxlen; if (key->type == Key::MULTIPLE) { /* not a critical problem */ @@ -2871,6 +2853,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, length); push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_TOO_LONG_KEY, warn_buff); + /* Align key length to multibyte char boundary */ + length-= length % sql_field->charset->mbmaxlen; } else { @@ -3448,7 +3432,7 @@ bool mysql_create_table_no_lock(THD *thd, (void) rm_temporary_table(create_info->db_type, path); goto unlock_and_end; } - thd->tmp_table_used= 1; + thd->thread_specific_used= TRUE; } /* @@ -4009,7 +3993,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, Item *item; Protocol *protocol= thd->protocol; LEX *lex= thd->lex; - int result_code, disable_logs= 0; + int result_code; DBUG_ENTER("mysql_admin_table"); if (end_active_trans(thd)) @@ -4057,22 +4041,6 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, 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; @@ -4142,8 +4110,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, } /* Close all instances of the table to allow repair to rename files */ - if (lock_type == TL_WRITE && table->table->s->version && - !table->table->s->log_table) + if (lock_type == TL_WRITE && table->table->s->version) { pthread_mutex_lock(&LOCK_open); const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open, @@ -4154,6 +4121,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, RTFC_WAIT_OTHER_THREAD_FLAG | RTFC_CHECK_KILLED_FLAG); thd->exit_cond(old_message); + DBUG_EXECUTE_IF("wait_in_mysql_admin_table", wait_for_kill_signal(thd);); if (thd->killed) goto err; /* Flush entries in the query cache involving this table. */ @@ -4173,7 +4141,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, goto err; } - if (operator_func == &handler::ha_repair) + if (operator_func == &handler::ha_repair && + !(check_opt->sql_flags & TT_USEFRM)) { if ((table->table->file->check_old_types() == HA_ADMIN_NEEDS_ALTER) || (table->table->file->ha_check_for_upgrade(check_opt) == @@ -4301,7 +4270,7 @@ send_result_message: close_thread_tables(thd); if (!result_code) // recreation went ok { - if ((table->table= open_ltable(thd, table, lock_type)) && + if ((table->table= open_ltable(thd, table, lock_type, 0)) && ((result_code= table->table->file->analyze(thd, check_opt)) > 0)) result_code= 0; // analyze went ok } @@ -4367,10 +4336,9 @@ send_result_message: } if (table->table) { - /* in the below check we do not refresh the log tables */ if (fatal_error) table->table->s->version=0; // Force close of table - else if (open_for_modify && !table->table->s->log_table) + else if (open_for_modify) { if (table->table->s->tmp_table) table->table->file->info(HA_STATUS_CONST); @@ -4393,24 +4361,11 @@ 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); @@ -4698,8 +4653,11 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, DBUG_EXECUTE_IF("sleep_create_like_before_ha_create", my_sleep(6000000);); dst_path[dst_path_length - reg_ext_length]= '\0'; // Remove .frm + if (thd->variables.keep_files_on_create) + create_info->options|= HA_CREATE_KEEP_FILES; err= ha_create_table(thd, dst_path, db, table_name, create_info, 1); VOID(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)) @@ -4852,7 +4810,7 @@ mysql_discard_or_import_tablespace(THD *thd, not complain when we lock the table */ thd->tablespace_op= TRUE; - if (!(table=open_ltable(thd,table_list,TL_WRITE))) + if (!(table=open_ltable(thd, table_list, TL_WRITE, 0))) { thd->tablespace_op=FALSE; DBUG_RETURN(-1); @@ -5770,7 +5728,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, #ifdef WITH_PARTITION_STORAGE_ENGINE if (alter_info->flags & ALTER_PARTITION) { - my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table"); + my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table"); DBUG_RETURN(TRUE); } #endif @@ -5859,7 +5817,7 @@ view_err: start_waiting_global_read_lock(thd); DBUG_RETURN(error); } - if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ))) + if (!(table=open_ltable(thd, table_list, TL_WRITE_ALLOW_READ, 0))) DBUG_RETURN(TRUE); table->use_all_columns(); @@ -6375,11 +6333,9 @@ view_err: { 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); + VOID(pthread_mutex_unlock(&LOCK_open)); 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)); error= ha_commit_stmt(thd); if (ha_commit(thd)) error= 1; @@ -6810,7 +6766,6 @@ copy_data_between_tables(TABLE *from,TABLE *to, 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.stmt= FALSE; thd->abort_on_warning= !ignore && test(thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)); @@ -7027,7 +6982,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, strxmov(table_name, table->db ,".", table->table_name, NullS); - t= table->table= open_ltable(thd, table, TL_READ); + t= table->table= open_ltable(thd, table, TL_READ, 0); thd->clear_error(); // these errors shouldn't get client protocol->prepare_for_resend(); diff --git a/sql/sql_test.cc b/sql/sql_test.cc index 6d75c90eb1d..f1d7e4a7312 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -509,7 +509,7 @@ Next alarm time: %lu\n", fflush(stdout); my_checkmalloc(); fprintf(stdout,"\nBegin safemalloc memory dump:\n"); // tag needed for test suite - TERMINATE(stdout); // Write malloc information + TERMINATE(stdout, 1); // Write malloc information fprintf(stdout,"\nEnd safemalloc memory dump.\n"); #ifdef HAVE_MALLINFO diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 06dd0dded43..ce26b025430 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -1212,17 +1212,6 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, table->triggers= triggers; /* - Construct key that will represent triggers for this table in the set - of routines used by statement. - */ - triggers->sroutines_key.length= 1+strlen(db)+1+strlen(table_name)+1; - if (!(triggers->sroutines_key.str= (char*) - alloc_root(&table->mem_root, triggers->sroutines_key.length))) - DBUG_RETURN(1); - triggers->sroutines_key.str[0]= TYPE_ENUM_TRIGGER; - strxmov(triggers->sroutines_key.str+1, db, ".", table_name, NullS); - - /* TODO: This could be avoided if there is no triggers for UPDATE and DELETE. */ @@ -1270,6 +1259,15 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, DBUG_ASSERT(lex.sphead == 0); goto err_with_lex_cleanup; } + /* + Not strictly necessary to invoke this method here, since we know + that we've parsed CREATE TRIGGER and not an + UPDATE/DELETE/INSERT/REPLACE/LOAD/CREATE TABLE, but we try to + maintain the invariant that this method is called for each + distinct statement, in case its logic is extended with other + types of analyses in future. + */ + lex.set_trg_event_type_for_tables(); lex.sphead->set_info(0, 0, &lex.sp_chistics, (ulong) *trg_sql_mode); @@ -1606,8 +1604,6 @@ bool Table_triggers_list::drop_all_triggers(THD *thd, char *db, char *name) bzero(&table, sizeof(table)); init_alloc_root(&table.mem_root, 8192, 0); - safe_mutex_assert_owner(&LOCK_open); - if (Table_triggers_list::check_n_load(thd, db, name, &table, 1)) { result= 1; @@ -1774,26 +1770,24 @@ Table_triggers_list::change_table_name_in_trignames(const char *db_name, } -/* - Update .TRG and .TRN files after renaming triggers' subject table. +/** + @brief Update .TRG and .TRN files after renaming triggers' subject table. - SYNOPSIS - change_table_name() - thd Thread context - db Old database of subject table - old_table Old name of subject table - new_db New database for subject table - new_table New name of subject table + @param[in,out] thd Thread context + @param[in] db Old database of subject table + @param[in] old_table Old name of subject table + @param[in] new_db New database for subject table + @param[in] new_table New name of subject table - NOTE + @note This method tries to leave trigger related files in consistent state, i.e. it either will complete successfully, or will fail leaving files in their initial state. Also this method assumes that subject table is not renamed to itself. + This method needs to be called under an exclusive table name lock. - RETURN VALUE - FALSE Success - TRUE Error + @retval FALSE Success + @retval TRUE Error */ bool Table_triggers_list::change_table_name(THD *thd, const char *db, @@ -1809,7 +1803,19 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, bzero(&table, sizeof(table)); init_alloc_root(&table.mem_root, 8192, 0); - safe_mutex_assert_owner(&LOCK_open); + /* + This method interfaces the mysql server code protected by + either LOCK_open mutex or with an exclusive table name lock. + In the future, only an exclusive table name lock will be enough. + */ +#ifndef DBUG_OFF + uchar key[MAX_DBKEY_LENGTH]; + uint key_length= (uint) (strmov(strmov((char*)&key[0], db)+1, + old_table)-(char*)&key[0])+1; + + if (!is_table_name_exclusively_locked_by_this_thread(thd, key, key_length)) + safe_mutex_assert_owner(&LOCK_open); +#endif DBUG_ASSERT(my_strcasecmp(table_alias_charset, db, new_db) || my_strcasecmp(table_alias_charset, old_table, new_table)); @@ -1876,7 +1882,7 @@ end: @param thd @param event - @param time_type, + @param time_type @param old_row_is_record1 @return Error status. @@ -1891,8 +1897,9 @@ bool Table_triggers_list::process_triggers(THD *thd, { bool err_status; Sub_statement_state statement_state; + sp_head *sp_trigger= bodies[event][time_type]; - if (!bodies[event][time_type]) + if (sp_trigger == NULL) return FALSE; if (old_row_is_record1) @@ -1905,15 +1912,20 @@ bool Table_triggers_list::process_triggers(THD *thd, new_field= record1_field; old_field= trigger_table->field; } + /* + This trigger must have been processed by the pre-locking + algorithm. + */ + DBUG_ASSERT(trigger_table->pos_in_table_list->trg_event_map & + static_cast<uint>(1 << static_cast<int>(event))); thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER); err_status= - bodies[event][time_type]->execute_trigger( - thd, - &trigger_table->s->db, - &trigger_table->s->table_name, - &subject_table_grants[event][time_type]); + sp_trigger->execute_trigger(thd, + &trigger_table->s->db, + &trigger_table->s->table_name, + &subject_table_grants[event][time_type]); thd->restore_sub_statement_state(&statement_state); @@ -1927,7 +1939,7 @@ bool Table_triggers_list::process_triggers(THD *thd, SYNOPSIS mark_fields_used() thd Current thread context - event Type of event triggers for which we are going to inspect + event Type of event triggers for which we are going to ins DESCRIPTION This method marks fields of subject table which are read/set in its @@ -2062,9 +2074,9 @@ process_unknown_string(char *&unknown_key, uchar* base, MEM_ROOT *mem_root, /** Contruct path to TRN-file. - @param thd[in] Thread context. - @param trg_name[in] Trigger name. - @param trn_path[out] Variable to store constructed path + @param[in] thd Thread context. + @param[in] trg_name Trigger name. + @param[out] trn_path Variable to store constructed path */ void build_trn_path(THD *thd, const sp_name *trg_name, LEX_STRING *trn_path) @@ -2097,10 +2109,10 @@ bool check_trn_exists(const LEX_STRING *trn_path) /** Retrieve table name for given trigger. - @param thd[in] Thread context. - @param trg_name[in] Trigger name. - @param trn_path[in] Path to the corresponding TRN-file. - @param tbl_name[out] Variable to store retrieved table name. + @param[in] thd Thread context. + @param[in] trg_name Trigger name. + @param[in] trn_path Path to the corresponding TRN-file. + @param[out] tbl_name Variable to store retrieved table name. @return Error status. @retval FALSE on success. diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index bfdbae12bdc..8f6b08c927f 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -56,14 +56,6 @@ class Table_triggers_list: public Sql_alloc updating trigger definitions during RENAME TABLE. */ List<LEX_STRING> on_table_names_list; - /* - Key representing triggers for this table in set of all stored - routines used by statement. - TODO: We won't need this member once triggers namespace will be - database-wide instead of table-wide because then we will be able - to use key based on sp_name as for other stored routines. - */ - LEX_STRING sroutines_key; /* Grant information for each trigger (pair: subject table, trigger definer). diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 8361fc64f33..19582af38f4 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -472,7 +472,7 @@ int mysql_create_function(THD *thd,udf_func *udf) tables.db= (char*) "mysql"; tables.table_name= tables.alias= (char*) "func"; /* Allow creation of functions even if we can't open func table */ - if (!(table = open_ltable(thd,&tables,TL_WRITE))) + if (!(table = open_ltable(thd, &tables, TL_WRITE, 0))) goto err; table->use_all_columns(); restore_record(table, s->default_values); // Default values for fields @@ -547,13 +547,14 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) bzero((char*) &tables,sizeof(tables)); tables.db=(char*) "mysql"; tables.table_name= tables.alias= (char*) "func"; - if (!(table = open_ltable(thd,&tables,TL_WRITE))) + if (!(table = open_ltable(thd, &tables, TL_WRITE, 0))) goto err; table->use_all_columns(); table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin); - if (!table->file->index_read_idx(table->record[0], 0, - (uchar*) table->field[0]->ptr, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (!table->file->index_read_idx_map(table->record[0], 0, + (uchar*) table->field[0]->ptr, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { int error; if ((error = table->file->ha_delete_row(table->record[0]))) diff --git a/sql/sql_udf.h b/sql/sql_udf.h index 3cd9343610c..4b8b492698e 100644 --- a/sql/sql_udf.h +++ b/sql/sql_udf.h @@ -47,7 +47,6 @@ typedef struct st_udf_func } udf_func; class Item_result_field; -struct st_table_list; class udf_handler :public Sql_alloc { diff --git a/sql/sql_union.cc b/sql/sql_union.cc index c872d3cb241..5da4b97cc5d 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -547,6 +547,10 @@ bool st_select_lex_unit::exec() /* allocate JOIN for fake select only once (prevent mysql_select automatic allocation) + TODO: The above is nonsense. mysql_select() will not allocate the + join if one already exists. There must be some other reason why we + don't let it allocate the join. Perhaps this is because we need + some special parameter values passed to join constructor? */ if (!(fake_select_lex->join= new JOIN(thd, item_list, fake_select_lex->options, result))) @@ -554,33 +558,52 @@ bool st_select_lex_unit::exec() fake_select_lex->table_list.empty(); DBUG_RETURN(TRUE); } + fake_select_lex->join->no_const_tables= TRUE; /* Fake st_select_lex should have item list for correctref_array allocation. */ fake_select_lex->item_list= item_list; + 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); } else { - JOIN_TAB *tab,*end; - for (tab=join->join_tab, end=tab+join->tables ; - tab && tab != end ; - tab++) - { - delete tab->select; - delete tab->quick; - } - join->init(thd, item_list, fake_select_lex->options, result); + if (describe) + { + /* + In EXPLAIN command, constant subqueries that do not use any + tables are executed two times: + - 1st time is a real evaluation to get the subquery value + - 2nd time is to produce EXPLAIN output rows. + 1st execution sets certain members (e.g. select_result) to perform + subquery execution rather than EXPLAIN line production. In order + to reset them back, we re-do all of the actions (yes it is ugly): + */ + join->init(thd, item_list, fake_select_lex->options, result); + 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); + } + else + { + join->examined_rows= 0; + saved_error= join->reinit(); + join->exec(); + } } - 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 (!saved_error) diff --git a/sql/sql_update.cc b/sql/sql_update.cc index ef1f46bfdd2..4071bb86c90 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -83,6 +83,75 @@ static bool check_fields(THD *thd, List<Item> &items) } +/** + @brief Re-read record if more columns are needed for error message. + + If we got a duplicate key error, we want to write an error + message containing the value of the duplicate key. If we do not have + all fields of the key value in record[0], we need to re-read the + record with a proper read_set. + + @param[in] error error number + @param[in] table table +*/ + +static void prepare_record_for_error_message(int error, TABLE *table) +{ + Field **field_p; + Field *field; + uint keynr; + MY_BITMAP unique_map; /* Fields in offended unique. */ + my_bitmap_map unique_map_buf[bitmap_buffer_size(MAX_FIELDS)]; + DBUG_ENTER("prepare_record_for_error_message"); + + /* + Only duplicate key errors print the key value. + If storage engine does always read all columns, we have the value alraedy. + */ + if ((error != HA_ERR_FOUND_DUPP_KEY) || + !(table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ)) + DBUG_VOID_RETURN; + + /* + Get the number of the offended index. + We will see MAX_KEY if the engine cannot determine the affected index. + */ + if ((keynr= table->file->get_dup_key(error)) >= MAX_KEY) + DBUG_VOID_RETURN; + + /* Create unique_map with all fields used by that index. */ + bitmap_init(&unique_map, unique_map_buf, table->s->fields, FALSE); + table->mark_columns_used_by_index_no_reset(keynr, &unique_map); + + /* Subtract read_set and write_set. */ + bitmap_subtract(&unique_map, table->read_set); + bitmap_subtract(&unique_map, table->write_set); + + /* + If the unique index uses columns that are neither in read_set + nor in write_set, we must re-read the record. + Otherwise no need to do anything. + */ + if (bitmap_is_clear_all(&unique_map)) + DBUG_VOID_RETURN; + + /* Get identifier of last read record into table->file->ref. */ + table->file->position(table->record[0]); + /* Add all fields used by unique index to read_set. */ + bitmap_union(table->read_set, &unique_map); + /* Tell the engine about the new set. */ + table->file->column_bitmaps_signal(); + /* Read record that is identified by table->file->ref. */ + (void) table->file->rnd_pos(table->record[1], table->file->ref); + /* Copy the newly read columns into the new record. */ + for (field_p= table->field; (field= *field_p); field_p++) + if (bitmap_is_set(&unique_map, field->field_index)) + field->copy_from_tmp(table->s->rec_buff_length); + + DBUG_VOID_RETURN; +} + + /* Process usual UPDATE @@ -231,6 +300,17 @@ int mysql_update(THD *thd, if (cond_value == Item::COND_FALSE) limit= 0; // Impossible WHERE } + + /* + If a timestamp field settable on UPDATE is present then to avoid wrong + update force the table handler to retrieve write-only fields to be able + to compare records and detect data change. + */ + if (table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ && + table->timestamp_field && + (table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_UPDATE || + table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_BOTH)) + bitmap_union(table->read_set, table->write_set); // Don't count on usage of 'only index' when calculating which key to use table->covering_keys.clear_all(); @@ -381,7 +461,7 @@ int mysql_update(THD *thd, init_read_record_idx(&info, thd, table, 1, used_index); thd->proc_info="Searching rows for update"; - uint tmp_limit= limit; + ha_rows tmp_limit= limit; while (!(error=info.read_record(&info)) && !thd->killed) { @@ -450,7 +530,6 @@ int mysql_update(THD *thd, thd->proc_info="Updating"; transactional_table= table->file->has_transactions(); - thd->no_trans_update.stmt= FALSE; thd->abort_on_warning= test(!ignore && (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES | @@ -471,6 +550,13 @@ int mysql_update(THD *thd, will_batch= !table->file->start_bulk_update(); /* + Assure that we can use position() + if we need to create an error message. + */ + if (table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) + table->prepare_for_position(); + + /* We can use compare_record() to optimize away updates if the table handler is returning all columns OR if if all updated columns are read @@ -554,7 +640,6 @@ int mysql_update(THD *thd, updated++; else error= 0; - thd->no_trans_update.stmt= !transactional_table; if (table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, @@ -573,6 +658,8 @@ int mysql_update(THD *thd, */ if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)) thd->fatal_error(); /* Other handler errors are fatal */ + + prepare_record_for_error_message(error, table); table->file->print_error(error,MYF(0)); error= 1; break; @@ -596,13 +683,16 @@ int mysql_update(THD *thd, { if (error) { + /* purecov: begin inspected */ /* The handler should not report error of duplicate keys if they are ignored. This is a requirement on batching handlers. */ + prepare_record_for_error_message(error, table); table->file->print_error(error,MYF(0)); error= 1; break; + /* purecov: end */ } /* Either an error was found and we are ignoring errors or there @@ -625,6 +715,10 @@ int mysql_update(THD *thd, } dup_key_found= 0; + if (!transactional_table && updated > 0) + thd->transaction.stmt.modified_non_trans_table= TRUE; + + /* todo bug#27571: to avoid asynchronization of `error' and `error_code' of binlog event constructor @@ -668,9 +762,12 @@ int mysql_update(THD *thd, in the batched update. */ { + /* purecov: begin inspected */ thd->fatal_error(); + prepare_record_for_error_message(loc_error, table); table->file->print_error(loc_error,MYF(0)); error= 1; + /* purecov: end */ } else updated-= dup_key_found; @@ -714,9 +811,10 @@ int mysql_update(THD *thd, error=1; // Rollback update } } - if (!transactional_table) - thd->no_trans_update.all= TRUE; + if (thd->transaction.stmt.modified_non_trans_table) + thd->transaction.all.modified_non_trans_table= TRUE; } + DBUG_ASSERT(transactional_table || !updated || thd->transaction.stmt.modified_non_trans_table); free_underlaid_joins(thd, select_lex); if (transactional_table) { @@ -1081,7 +1179,6 @@ bool mysql_multi_update(THD *thd, handle_duplicates, ignore))) DBUG_RETURN(TRUE); - thd->no_trans_update.stmt= FALSE; thd->abort_on_warning= test(thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)); @@ -1454,9 +1551,8 @@ multi_update::~multi_update() if (copy_field) delete [] copy_field; thd->count_cuted_fields= CHECK_FIELD_IGNORE; // Restore this setting - if (!trans_safe) // todo: remove since redundant - thd->no_trans_update.all= TRUE; - DBUG_ASSERT(trans_safe || thd->no_trans_update.all); + DBUG_ASSERT(trans_safe || !updated || + thd->transaction.all.modified_non_trans_table); } @@ -1540,6 +1636,8 @@ bool multi_update::send_data(List<Item> ¬_used_values) */ if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)) thd->fatal_error(); /* Other handler errors are fatal */ + + prepare_record_for_error_message(error, table); table->file->print_error(error,MYF(0)); DBUG_RETURN(1); } @@ -1558,7 +1656,7 @@ bool multi_update::send_data(List<Item> ¬_used_values) else { trans_safe= 0; - thd->no_trans_update.stmt= TRUE; + thd->transaction.stmt.modified_non_trans_table= TRUE; } if (table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, @@ -1621,7 +1719,6 @@ void multi_update::send_error(uint errcode,const char *err) /* Something already updated so we have to invalidate cache */ query_cache_invalidate3(thd, update_tables, 1); - /* If all tables that has been updated are trans safe then just do rollback. If not attempt to do remaining updates. @@ -1634,7 +1731,7 @@ void multi_update::send_error(uint errcode,const char *err) } else { - DBUG_ASSERT(thd->no_trans_update.stmt); + DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table); if (do_update && table_count > 1) { /* Add warning here */ @@ -1645,7 +1742,7 @@ void multi_update::send_error(uint errcode,const char *err) VOID(do_updates(0)); } } - if (thd->no_trans_update.stmt) + if (thd->transaction.stmt.modified_non_trans_table) { /* The query has to binlog because there's a modified non-transactional table @@ -1658,9 +1755,9 @@ void multi_update::send_error(uint errcode,const char *err) transactional_tables, FALSE); } if (!trans_safe) - thd->no_trans_update.all= TRUE; + thd->transaction.all.modified_non_trans_table= TRUE; } - DBUG_ASSERT(trans_safe || !updated || thd->no_trans_update.stmt); + DBUG_ASSERT(trans_safe || !updated || thd->transaction.stmt.modified_non_trans_table); if (transactional_tables) { @@ -1676,7 +1773,7 @@ int multi_update::do_updates(bool from_send_error) ha_rows org_updated; TABLE *table, *tmp_table; List_iterator_fast<TABLE> check_opt_it(unupdated_check_opt_tables); - DBUG_ENTER("do_updates"); + DBUG_ENTER("multi_update::do_updates"); do_update= 0; // Don't retry this function if (!found) @@ -1803,7 +1900,7 @@ int multi_update::do_updates(bool from_send_error) else { trans_safe= 0; // Can't do safe rollback - thd->no_trans_update.stmt= TRUE; + thd->transaction.stmt.modified_non_trans_table= TRUE; } } (void) table->file->ha_rnd_end(); @@ -1819,6 +1916,7 @@ err: if (!from_send_error) { thd->fatal_error(); + prepare_record_for_error_message(local_error, table); table->file->print_error(local_error,MYF(0)); } @@ -1836,7 +1934,7 @@ err2: else { trans_safe= 0; - thd->no_trans_update.stmt= TRUE; + thd->transaction.stmt.modified_non_trans_table= TRUE; } } DBUG_RETURN(1); @@ -1849,6 +1947,7 @@ bool multi_update::send_eof() { char buff[STRING_BUFFER_USUAL_SIZE]; ulonglong id; + DBUG_ENTER("multi_update::send_eof"); thd->proc_info="updating reference tables"; /* Does updates for the last n - 1 tables, returns 0 if ok */ @@ -1862,7 +1961,6 @@ bool multi_update::send_eof() { query_cache_invalidate3(thd, update_tables, 1); } - /* Write the SQL statement to the binlog if we updated rows and we succeeded or if we updated some non @@ -1872,8 +1970,9 @@ bool multi_update::send_eof() either from the query's list or via a stored routine: bug#13270,23333 */ - DBUG_ASSERT(trans_safe || !updated || thd->no_trans_update.stmt); - if (local_error == 0 || thd->no_trans_update.stmt) + DBUG_ASSERT(trans_safe || !updated || + thd->transaction.stmt.modified_non_trans_table); + if (local_error == 0 || thd->transaction.stmt.modified_non_trans_table) { if (mysql_bin_log.is_open()) { @@ -1889,8 +1988,8 @@ bool multi_update::send_eof() local_error= 1; // Rollback update } } - if (!trans_safe) - thd->no_trans_update.all= TRUE; + if (thd->transaction.stmt.modified_non_trans_table) + thd->transaction.all.modified_non_trans_table= TRUE; } if (transactional_tables) @@ -1904,7 +2003,7 @@ bool multi_update::send_eof() /* Safety: If we haven't got an error before (can happen in do_updates) */ my_message(ER_UNKNOWN_ERROR, "An error occured in multi-table update", MYF(0)); - return TRUE; + DBUG_RETURN(TRUE); } id= thd->arg_of_last_insert_id_function ? @@ -1914,5 +2013,5 @@ bool multi_update::send_eof() thd->row_count_func= (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated; ::send_ok(thd, (ulong) thd->row_count_func, id, buff); - return FALSE; + DBUG_RETURN(FALSE); } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index ce311f5d4a2..56d50761d95 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -397,7 +397,13 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, { Item_field *field; if ((field= item->filed_for_view_update())) + { + /* + any_privileges may be reset later by the Item_field::set_field + method in case of a system temporary table. + */ field->any_privileges= 1; + } } } #endif @@ -1038,10 +1044,21 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, table->view= lex= thd->lex= (LEX*) new(thd->mem_root) st_lex_local; { + char old_db_buf[NAME_LEN+1]; + LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) }; + bool dbchanged; Lex_input_stream lip(thd, table->select_stmt.str, table->select_stmt.length); + /* + Use view db name as thread default database, in order to ensure + that the view is parsed and prepared correctly. + */ + if ((result= mysql_opt_change_db(thd, &table->view_db, &old_db, 1, + &dbchanged))) + goto end; + lex_start(thd); view_select= &lex->select_lex; view_select->select_number= ++thd->select_number; @@ -1085,6 +1102,9 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, lex->sql_command= old_lex->sql_command; thd->variables.sql_mode= saved_mode; + + if (dbchanged && mysql_change_db(thd, &old_db, TRUE)) + goto err; } if (!parse_status) { @@ -1199,7 +1219,20 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, */ for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local) tbl->lock_type= table->lock_type; + /* + If the view is mergeable, we might want to + INSERT/UPDATE/DELETE into tables of this view. Preserve the + original sql command and 'duplicates' of the outer lex. + This is used later in set_trg_event_type_for_command. + */ + lex->sql_command= old_lex->sql_command; + lex->duplicates= old_lex->duplicates; } + /* + This method has a dependency on the proper lock type being set, + so in case of views should be called here. + */ + lex->set_trg_event_type_for_tables(); /* If we are opening this view as part of implicit LOCK TABLES, then diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e6b0aeb6430..560aebcc456 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -15,8 +15,13 @@ /* sql_yacc.yy */ +/** + @defgroup Parser Parser + @{ +*/ + %{ -/* thd is passed as an arg to yyparse(), and subsequently to yylex(). +/* thd is passed as an argument to yyparse(), and subsequently to yylex(). ** The type will be void*, so it must be cast to (THD*) when used. ** Use the YYTHD macro for this. */ @@ -26,7 +31,7 @@ #define MYSQL_YACC #define YYINITDEPTH 100 -#define YYMAXDEPTH 3200 /* Because of 64K stack */ +#define YYMAXDEPTH 3200 /* Because of 64K stack */ #define Lex (YYTHD->lex) #define Select Lex->current_select #include "mysql_priv.h" @@ -43,15 +48,27 @@ int yylex(void *yylval, void *yythd); -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; }} +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 MYSQL_YYABORT \ do \ - { \ + { \ LEX::cleanup_lex_after_parse_error(YYTHD);\ - YYABORT; \ + YYABORT; \ } while (0) #define MYSQL_YYABORT_UNLESS(A) \ @@ -490,12 +507,12 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal, bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %} -%pure_parser /* We have threads */ +%pure_parser /* We have threads */ /* - Currently there is 286 shift/reduce conflict. We should not introduce - new conflicts any more. + Currently there are 280 shift/reduce conflicts. + We should not introduce new conflicts any more. */ -%expect 286 +%expect 280 /* Comments for TOKENS. @@ -1077,43 +1094,45 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); /* A dummy token to force the priority of table_ref production in a join. */ %left TABLE_REF_PRIORITY %left SET_VAR -%left OR_OR_SYM OR_SYM OR2_SYM XOR -%left AND_SYM AND_AND_SYM -%left BETWEEN_SYM CASE_SYM WHEN_SYM THEN_SYM ELSE -%left EQ EQUAL_SYM GE GT_SYM LE LT NE IS LIKE REGEXP IN_SYM -%left '|' -%left '&' -%left SHIFT_LEFT SHIFT_RIGHT -%left '-' '+' -%left '*' '/' '%' DIV_SYM MOD_SYM +%left OR_OR_SYM OR_SYM OR2_SYM +%left XOR +%left AND_SYM AND_AND_SYM +%left BETWEEN_SYM CASE_SYM WHEN_SYM THEN_SYM ELSE +%left EQ EQUAL_SYM GE GT_SYM LE LT NE IS LIKE REGEXP IN_SYM +%left '|' +%left '&' +%left SHIFT_LEFT SHIFT_RIGHT +%left '-' '+' +%left '*' '/' '%' DIV_SYM MOD_SYM %left '^' -%left NEG '~' -%right NOT_SYM NOT2_SYM -%right BINARY COLLATE_SYM +%left NEG '~' +%right NOT_SYM NOT2_SYM +%right BINARY COLLATE_SYM +%left INTERVAL_SYM %type <lex_str> IDENT IDENT_QUOTED TEXT_STRING DECIMAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM - LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text + LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text IDENT_sys TEXT_STRING_sys TEXT_STRING_literal - NCHAR_STRING opt_component key_cache_name + NCHAR_STRING opt_component key_cache_name sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty %type <lex_str_ptr> - opt_table_alias + opt_table_alias %type <table> - table_ident table_ident_nodb references xid + table_ident table_ident_nodb references xid %type <simple_string> - remember_name remember_end opt_ident opt_db text_or_password - opt_constraint constraint + remember_name remember_end opt_ident opt_db text_or_password + opt_constraint constraint %type <string> - text_string opt_gconcat_separator + text_string opt_gconcat_separator %type <num> - type int_type real_type order_dir lock_option - udf_type if_exists opt_local opt_table_options table_options + type int_type real_type order_dir lock_option + udf_type if_exists opt_local opt_table_options table_options table_option opt_if_not_exists opt_no_write_to_binlog delete_option opt_temporary all_or_any opt_distinct opt_ignore_leaves fulltext_options spatial_type union_option @@ -1124,10 +1143,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt %type <ulong_num> - ulong_num real_ulong_num merge_insert_types + ulong_num real_ulong_num merge_insert_types %type <ulonglong_number> - ulonglong_num real_ulonglong_num size_number + ulonglong_num real_ulonglong_num size_number %type <choice> choice @@ -1135,19 +1154,19 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); part_bit_expr %type <lock_type> - replace_lock_option opt_low_priority insert_lock_option load_data_lock + replace_lock_option opt_low_priority insert_lock_option load_data_lock %type <item> - literal text_literal insert_ident order_ident - simple_ident select_item2 expr opt_expr opt_else sum_expr in_sum_expr - 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 - param_marker geometry_function - signed_literal now_or_signed_literal opt_escape - sp_opt_default - simple_ident_nospvar simple_ident_q + literal text_literal insert_ident order_ident + simple_ident select_item2 expr opt_expr opt_else sum_expr in_sum_expr + variable variable_aux bool_pri + predicate bit_expr + table_wild simple_expr udf_expr + expr_or_default set_expr_or_default interval_expr + param_marker geometry_function + signed_literal now_or_signed_literal opt_escape + sp_opt_default + simple_ident_nospvar simple_ident_q field_or_var limit_option part_func_expr function_call_keyword @@ -1156,29 +1175,29 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); function_call_conflict %type <item_num> - NUM_literal + NUM_literal %type <item_list> - expr_list udf_expr_list udf_expr_list2 when_list - ident_list ident_list_arg opt_expr_list + expr_list opt_udf_expr_list udf_expr_list when_list + ident_list ident_list_arg opt_expr_list %type <var_type> option_type opt_var_type opt_var_ident_type %type <key_type> - key_type opt_unique_or_fulltext constraint_key_type + key_type opt_unique_or_fulltext constraint_key_type %type <key_alg> - btree_or_rtree + btree_or_rtree %type <string_list> - using_list + using_list %type <key_part> - key_part + key_part %type <table_list> - join_table_list join_table + join_table_list join_table table_factor table_ref select_derived derived_table_list @@ -1204,58 +1223,59 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <lex_user> user grant_user %type <charset> - opt_collate - charset_name - charset_name_or_default - old_or_new_charset_name - old_or_new_charset_name_or_default - collation_name - collation_name_or_default - opt_load_data_charset + opt_collate + charset_name + charset_name_or_default + old_or_new_charset_name + old_or_new_charset_name_or_default + collation_name + collation_name_or_default + opt_load_data_charset UNDERSCORE_CHARSET %type <variable> internal_variable_name %type <select_lex> subselect subselect_init - get_select_lex + get_select_lex %type <boolfunc2creator> comp_op %type <NONE> - query verb_clause create change select do drop insert replace insert2 - insert_values update delete truncate rename - show describe load alter optimize keycache preload flush - reset purge begin commit rollback savepoint release - slave master_def master_defs master_file_def slave_until_opts - repair restore backup analyze check start checksum - field_list field_list_item field_spec kill column_def key_def - keycache_list assign_to_keycache preload_list preload_keys - 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 - 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 - ref_list opt_on_delete opt_on_delete_list opt_on_delete_item use - opt_delete_options opt_delete_option varchar nchar nvarchar - opt_outer table_list table_name opt_option opt_place - opt_attribute opt_attribute_list attribute column_list column_list_id - opt_column_list grant_privileges grant_ident grant_list grant_option - object_privilege object_privilege_list user_list rename_list - clear_privileges flush_options flush_option - equal optional_braces - opt_mi_check_type opt_to mi_check_types normal_join - db_to_db table_to_table_list table_to_table opt_table_list opt_as - handler_rkey_function handler_read_or_scan - single_multi table_wild_list table_wild_one opt_wild - union_clause union_list - precision subselect_start opt_and charset - subselect_end select_var_list select_var_list_init help opt_len - opt_extended_describe + query verb_clause create change select do drop insert replace insert2 + insert_values update delete truncate rename + show describe load alter optimize keycache preload flush + reset purge begin commit rollback savepoint release + slave master_def master_defs master_file_def slave_until_opts + repair restore backup analyze check start checksum + field_list field_list_item field_spec kill column_def key_def + keycache_list assign_to_keycache preload_list preload_keys + 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 + 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 + ref_list opt_on_delete opt_on_delete_list opt_on_delete_item use + opt_delete_options opt_delete_option varchar nchar nvarchar + opt_outer table_list table_name table_alias_ref_list table_alias_ref + opt_option opt_place + opt_attribute opt_attribute_list attribute column_list column_list_id + opt_column_list grant_privileges grant_ident grant_list grant_option + object_privilege object_privilege_list user_list rename_list + clear_privileges flush_options flush_option + equal optional_braces + opt_mi_check_type opt_to mi_check_types normal_join + db_to_db table_to_table_list table_to_table opt_table_list opt_as + handler_rkey_function handler_read_or_scan + single_multi table_wild_list table_wild_one opt_wild + union_clause union_list + precision subselect_start opt_and charset + subselect_end select_var_list select_var_list_init help opt_len + opt_extended_describe prepare prepare_src execute deallocate - statement sp_suid - sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa + statement sp_suid + sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa load_data opt_field_or_var_spec fields_or_vars opt_load_data_set_spec definer view_replace_or_algorithm view_replace view_algorithm view_or_trigger_or_sp_or_event @@ -1263,7 +1283,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); view_suid view_tail view_list_opt view_list view_select 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 + init_key_options key_options key_opts key_opt key_using_alg server_def server_options_list server_option END_OF_INPUT @@ -1284,376 +1304,397 @@ END_OF_INPUT %type <num> index_hint_clause %type <NONE> - '-' '+' '*' '/' '%' '(' ')' - ',' '!' '{' '}' '&' '|' AND_SYM OR_SYM OR_OR_SYM BETWEEN_SYM CASE_SYM - THEN_SYM WHEN_SYM DIV_SYM MOD_SYM OR2_SYM AND_AND_SYM + '-' '+' '*' '/' '%' '(' ')' + ',' '!' '{' '}' '&' '|' AND_SYM OR_SYM OR_OR_SYM BETWEEN_SYM CASE_SYM + THEN_SYM WHEN_SYM DIV_SYM MOD_SYM OR2_SYM AND_AND_SYM %% +/* + Indentation of grammar rules: + +rule: <-- starts at col 1 + rule1a rule1b rule1c <-- starts at col 11 + { <-- starts at col 11 + code <-- starts at col 13, indentation is 2 spaces + } + | rule2a rule2b + { + code + } + ; <-- on a line by itself, starts at col 9 + + Also, please do not use any <TAB>, but spaces. + Having a uniform indentation in this file helps + code reviews, patches, merges, and make maintenance easier. + Tip: grep [[:cntrl:]] sql_yacc.yy + Thanks. +*/ query: - END_OF_INPUT - { - THD *thd= YYTHD; - if (!thd->bootstrap && - (!(thd->lex->select_lex.options & OPTION_FOUND_COMMENT))) - { - my_message(ER_EMPTY_QUERY, ER(ER_EMPTY_QUERY), MYF(0)); - MYSQL_YYABORT; - } - else - { - thd->lex->sql_command= SQLCOM_EMPTY_QUERY; - } - } - | verb_clause END_OF_INPUT {}; + END_OF_INPUT + { + THD *thd= YYTHD; + if (!thd->bootstrap && + (!(thd->lex->select_lex.options & OPTION_FOUND_COMMENT))) + { + my_message(ER_EMPTY_QUERY, ER(ER_EMPTY_QUERY), MYF(0)); + MYSQL_YYABORT; + } + else + { + thd->lex->sql_command= SQLCOM_EMPTY_QUERY; + } + } + | verb_clause END_OF_INPUT {} + ; verb_clause: - statement - | begin - ; + statement + | begin + ; /* Verb clauses, except begin */ statement: - alter - | analyze - | backup - | binlog_base64_event - | call - | change - | check - | checksum - | commit - | create + alter + | analyze + | backup + | binlog_base64_event + | call + | change + | check + | checksum + | commit + | create | deallocate - | delete - | describe - | do - | drop + | delete + | describe + | do + | drop | execute - | flush - | grant - | handler - | help - | insert + | flush + | grant + | handler + | help + | insert | install - | kill - | load - | lock - | optimize + | kill + | load + | lock + | optimize | keycache | partition_entry - | preload + | preload | prepare - | purge - | release - | rename - | repair - | replace - | reset - | restore - | revoke - | rollback - | savepoint - | select - | set - | show - | slave - | start - | truncate + | purge + | release + | rename + | repair + | replace + | reset + | restore + | revoke + | rollback + | savepoint + | select + | set + | show + | slave + | start + | truncate | uninstall - | unlock - | update - | use - | xa + | unlock + | update + | use + | xa ; deallocate: - deallocate_or_drop PREPARE_SYM ident - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - lex->sql_command= SQLCOM_DEALLOCATE_PREPARE; - lex->prepared_stmt_name= $3; - }; + deallocate_or_drop PREPARE_SYM ident + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + lex->sql_command= SQLCOM_DEALLOCATE_PREPARE; + lex->prepared_stmt_name= $3; + } + ; deallocate_or_drop: - DEALLOCATE_SYM | - DROP - ; - + DEALLOCATE_SYM + | DROP + ; prepare: - PREPARE_SYM ident FROM prepare_src - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - lex->sql_command= SQLCOM_PREPARE; - lex->prepared_stmt_name= $2; - }; + PREPARE_SYM ident FROM prepare_src + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + lex->sql_command= SQLCOM_PREPARE; + lex->prepared_stmt_name= $2; + } + ; prepare_src: - TEXT_STRING_sys - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - lex->prepared_stmt_code= $1; - lex->prepared_stmt_code_is_varref= FALSE; - } + TEXT_STRING_sys + { + 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; - LEX *lex= thd->lex; - lex->prepared_stmt_code= $2; - lex->prepared_stmt_code_is_varref= TRUE; - }; + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + lex->prepared_stmt_code= $2; + lex->prepared_stmt_code_is_varref= TRUE; + } + ; execute: - EXECUTE_SYM ident - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - lex->sql_command= SQLCOM_EXECUTE; - lex->prepared_stmt_name= $2; - } - execute_using - {} + EXECUTE_SYM ident + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + lex->sql_command= SQLCOM_EXECUTE; + lex->prepared_stmt_name= $2; + } + execute_using + {} ; execute_using: - /* nothing */ + /* nothing */ | USING execute_var_list ; execute_var_list: - execute_var_list ',' execute_var_ident + execute_var_list ',' execute_var_ident | execute_var_ident ; -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)) +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)) MYSQL_YYABORT; - } + } ; /* help */ help: - HELP_SYM - { - if (Lex->sphead) - { - my_error(ER_SP_BADSTATEMENT, MYF(0), "HELP"); - MYSQL_YYABORT; - } - } - ident_or_text - { - LEX *lex= Lex; - lex->sql_command= SQLCOM_HELP; - lex->help_arg= $3.str; - }; + HELP_SYM + { + if (Lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "HELP"); + MYSQL_YYABORT; + } + } + ident_or_text + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_HELP; + lex->help_arg= $3.str; + } + ; /* change master */ change: - CHANGE MASTER_SYM TO_SYM - { - LEX *lex = Lex; - lex->sql_command = SQLCOM_CHANGE_MASTER; - bzero((char*) &lex->mi, sizeof(lex->mi)); - } - master_defs - {} - ; + CHANGE MASTER_SYM TO_SYM + { + LEX *lex = Lex; + lex->sql_command = SQLCOM_CHANGE_MASTER; + bzero((char*) &lex->mi, sizeof(lex->mi)); + } + master_defs + {} + ; master_defs: - master_def - | master_defs ',' master_def; + master_def + | master_defs ',' master_def + ; master_def: - MASTER_HOST_SYM EQ TEXT_STRING_sys - { - Lex->mi.host = $3.str; - } - | - MASTER_USER_SYM EQ TEXT_STRING_sys - { - Lex->mi.user = $3.str; - } - | - MASTER_PASSWORD_SYM EQ TEXT_STRING_sys - { - Lex->mi.password = $3.str; - } - | - MASTER_PORT_SYM EQ ulong_num - { - Lex->mi.port = $3; - } - | - MASTER_CONNECT_RETRY_SYM EQ ulong_num - { - Lex->mi.connect_retry = $3; - } - | MASTER_SSL_SYM EQ ulong_num - { - Lex->mi.ssl= $3 ? - LEX_MASTER_INFO::SSL_ENABLE : LEX_MASTER_INFO::SSL_DISABLE; - } - | MASTER_SSL_CA_SYM EQ TEXT_STRING_sys - { - Lex->mi.ssl_ca= $3.str; - } - | MASTER_SSL_CAPATH_SYM EQ TEXT_STRING_sys - { - Lex->mi.ssl_capath= $3.str; - } - | MASTER_SSL_CERT_SYM EQ TEXT_STRING_sys - { - Lex->mi.ssl_cert= $3.str; - } - | MASTER_SSL_CIPHER_SYM EQ TEXT_STRING_sys - { - Lex->mi.ssl_cipher= $3.str; - } - | MASTER_SSL_KEY_SYM EQ TEXT_STRING_sys - { - Lex->mi.ssl_key= $3.str; - } - | MASTER_SSL_VERIFY_SERVER_CERT_SYM EQ ulong_num - { - Lex->mi.ssl_verify_server_cert= $3 ? - LEX_MASTER_INFO::SSL_ENABLE : LEX_MASTER_INFO::SSL_DISABLE; - } - | - master_file_def - ; + MASTER_HOST_SYM EQ TEXT_STRING_sys + { + Lex->mi.host = $3.str; + } + | MASTER_USER_SYM EQ TEXT_STRING_sys + { + Lex->mi.user = $3.str; + } + | MASTER_PASSWORD_SYM EQ TEXT_STRING_sys + { + Lex->mi.password = $3.str; + } + | MASTER_PORT_SYM EQ ulong_num + { + Lex->mi.port = $3; + } + | MASTER_CONNECT_RETRY_SYM EQ ulong_num + { + Lex->mi.connect_retry = $3; + } + | MASTER_SSL_SYM EQ ulong_num + { + Lex->mi.ssl= $3 ? + LEX_MASTER_INFO::SSL_ENABLE : LEX_MASTER_INFO::SSL_DISABLE; + } + | MASTER_SSL_CA_SYM EQ TEXT_STRING_sys + { + Lex->mi.ssl_ca= $3.str; + } + | MASTER_SSL_CAPATH_SYM EQ TEXT_STRING_sys + { + Lex->mi.ssl_capath= $3.str; + } + | MASTER_SSL_CERT_SYM EQ TEXT_STRING_sys + { + Lex->mi.ssl_cert= $3.str; + } + | MASTER_SSL_CIPHER_SYM EQ TEXT_STRING_sys + { + Lex->mi.ssl_cipher= $3.str; + } + | MASTER_SSL_KEY_SYM EQ TEXT_STRING_sys + { + Lex->mi.ssl_key= $3.str; + } + | MASTER_SSL_VERIFY_SERVER_CERT_SYM EQ ulong_num + { + Lex->mi.ssl_verify_server_cert= $3 ? + LEX_MASTER_INFO::SSL_ENABLE : LEX_MASTER_INFO::SSL_DISABLE; + } + | master_file_def + ; master_file_def: - MASTER_LOG_FILE_SYM EQ TEXT_STRING_sys - { - Lex->mi.log_file_name = $3.str; - } - | MASTER_LOG_POS_SYM EQ ulonglong_num - { - Lex->mi.pos = $3; - /* - If the user specified a value < BIN_LOG_HEADER_SIZE, adjust it - instead of causing subsequent errors. - We need to do it in this file, because only there we know that - MASTER_LOG_POS has been explicitely specified. On the contrary - in change_master() (sql_repl.cc) we cannot distinguish between 0 - (MASTER_LOG_POS explicitely specified as 0) and 0 (unspecified), - whereas we want to distinguish (specified 0 means "read the binlog - from 0" (4 in fact), unspecified means "don't change the position - (keep the preceding value)"). - */ - Lex->mi.pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.pos); - } - | RELAY_LOG_FILE_SYM EQ TEXT_STRING_sys - { - Lex->mi.relay_log_name = $3.str; - } - | RELAY_LOG_POS_SYM EQ ulong_num - { - Lex->mi.relay_log_pos = $3; - /* Adjust if < BIN_LOG_HEADER_SIZE (same comment as Lex->mi.pos) */ - Lex->mi.relay_log_pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.relay_log_pos); - } - ; + MASTER_LOG_FILE_SYM EQ TEXT_STRING_sys + { + Lex->mi.log_file_name = $3.str; + } + | MASTER_LOG_POS_SYM EQ ulonglong_num + { + Lex->mi.pos = $3; + /* + If the user specified a value < BIN_LOG_HEADER_SIZE, adjust it + instead of causing subsequent errors. + We need to do it in this file, because only there we know that + MASTER_LOG_POS has been explicitely specified. On the contrary + in change_master() (sql_repl.cc) we cannot distinguish between 0 + (MASTER_LOG_POS explicitely specified as 0) and 0 (unspecified), + whereas we want to distinguish (specified 0 means "read the binlog + from 0" (4 in fact), unspecified means "don't change the position + (keep the preceding value)"). + */ + Lex->mi.pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.pos); + } + | RELAY_LOG_FILE_SYM EQ TEXT_STRING_sys + { + Lex->mi.relay_log_name = $3.str; + } + | RELAY_LOG_POS_SYM EQ ulong_num + { + Lex->mi.relay_log_pos = $3; + /* Adjust if < BIN_LOG_HEADER_SIZE (same comment as Lex->mi.pos) */ + Lex->mi.relay_log_pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.relay_log_pos); + } + ; /* create a table */ create: - CREATE opt_table_options TABLE_SYM opt_if_not_exists table_ident - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - lex->sql_command= SQLCOM_CREATE_TABLE; - if (!lex->select_lex.add_table_to_list(thd, $5, NULL, - TL_OPTION_UPDATING, - TL_WRITE)) - MYSQL_YYABORT; - lex->alter_info.reset(); - lex->col_list.empty(); - lex->change=NullS; - bzero((char*) &lex->create_info,sizeof(lex->create_info)); - lex->create_info.options=$2 | $4; - lex->create_info.db_type= ha_default_handlerton(thd); - lex->create_info.default_table_charset= NULL; - lex->name.str= 0; - lex->name.length= 0; - } - create2 - { - LEX *lex= YYTHD->lex; - lex->current_select= &lex->select_lex; - if (!lex->create_info.db_type) - { - lex->create_info.db_type= ha_default_handlerton(YYTHD); - push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_USING_OTHER_HANDLER, - ER(ER_WARN_USING_OTHER_HANDLER), - ha_resolve_storage_engine_name(lex->create_info.db_type), - $5->table.str); - } - } - | CREATE opt_unique_or_fulltext INDEX_SYM ident key_alg ON - table_ident - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_CREATE_INDEX; - if (!lex->current_select->add_table_to_list(lex->thd, $7, - NULL, - TL_OPTION_UPDATING)) - MYSQL_YYABORT; + CREATE opt_table_options TABLE_SYM opt_if_not_exists table_ident + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + lex->sql_command= SQLCOM_CREATE_TABLE; + if (!lex->select_lex.add_table_to_list(thd, $5, NULL, + TL_OPTION_UPDATING, + TL_WRITE)) + MYSQL_YYABORT; + lex->alter_info.reset(); + lex->col_list.empty(); + lex->change=NullS; + bzero((char*) &lex->create_info,sizeof(lex->create_info)); + lex->create_info.options=$2 | $4; + lex->create_info.db_type= ha_default_handlerton(thd); + lex->create_info.default_table_charset= NULL; + lex->name.str= 0; + lex->name.length= 0; + } + create2 + { + LEX *lex= YYTHD->lex; + lex->current_select= &lex->select_lex; + if (!lex->create_info.db_type) + { + lex->create_info.db_type= ha_default_handlerton(YYTHD); + push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_USING_OTHER_HANDLER, + ER(ER_WARN_USING_OTHER_HANDLER), + ha_resolve_storage_engine_name(lex->create_info.db_type), + $5->table.str); + } + } + | CREATE opt_unique_or_fulltext INDEX_SYM ident key_alg ON + table_ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_CREATE_INDEX; + if (!lex->current_select->add_table_to_list(lex->thd, $7, + NULL, + TL_OPTION_UPDATING)) + MYSQL_YYABORT; lex->alter_info.reset(); lex->alter_info.flags= ALTER_ADD_INDEX; - lex->col_list.empty(); - lex->change=NullS; - } - '(' key_list ')' key_options - { - LEX *lex=Lex; + lex->col_list.empty(); + lex->change=NullS; + } + '(' key_list ')' key_options + { + LEX *lex=Lex; Key *key; - if ($2 != Key::FULLTEXT && lex->key_create_info.parser_name.str) - { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; - } + if ($2 != Key::FULLTEXT && lex->key_create_info.parser_name.str) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } key= new Key($2, $4.str, &lex->key_create_info, 0, lex->col_list); lex->alter_info.key_list.push_back(key); - lex->col_list.empty(); - } - | CREATE DATABASE opt_if_not_exists ident - { - Lex->create_info.default_table_charset= NULL; - Lex->create_info.used_fields= 0; - } - opt_create_database_options - { - LEX *lex=Lex; - lex->sql_command=SQLCOM_CREATE_DB; - lex->name= $4; + lex->col_list.empty(); + } + | CREATE DATABASE opt_if_not_exists ident + { + Lex->create_info.default_table_charset= NULL; + Lex->create_info.used_fields= 0; + } + opt_create_database_options + { + LEX *lex=Lex; + lex->sql_command=SQLCOM_CREATE_DB; + lex->name= $4; lex->create_info.options=$3; - } - | CREATE - { + } + | CREATE + { Lex->create_view_mode= VIEW_CREATE_NEW; Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; Lex->create_view_suid= TRUE; - } - view_or_trigger_or_sp_or_event - {} - | CREATE USER clear_privileges grant_list - { - Lex->sql_command = SQLCOM_CREATE_USER; } - | CREATE LOGFILE_SYM GROUP_SYM logfile_group_info + view_or_trigger_or_sp_or_event + {} + | CREATE USER clear_privileges grant_list + { + Lex->sql_command = SQLCOM_CREATE_USER; + } + | CREATE LOGFILE_SYM GROUP_SYM logfile_group_info { Lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP; } @@ -1661,67 +1702,66 @@ create: { Lex->alter_tablespace_info->ts_cmd_type= CREATE_TABLESPACE; } - | CREATE server_def - { - Lex->sql_command= SQLCOM_CREATE_SERVER; + | 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_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_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; - } + 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 /* - BE CAREFUL when you add a new rule to update the block where - YYTHD->client_capabilities is set back to original value + BE CAREFUL when you add a new rule to update the block where + YYTHD->client_capabilities is set back to original value */ { THD *thd= YYTHD; @@ -1765,10 +1805,11 @@ event_tail: */ Lex->sql_command= SQLCOM_CREATE_EVENT; } - ; + ; -ev_schedule_time: EVERY_SYM expr interval - { +ev_schedule_time: + EVERY_SYM expr interval + { Lex->event_parse_data->item_expression= $2; Lex->event_parse_data->interval= $3; } @@ -1780,7 +1821,8 @@ ev_schedule_time: EVERY_SYM expr interval } ; -opt_ev_status: /* empty */ { $$= 0; } +opt_ev_status: + /* empty */ { $$= 0; } | ENABLE_SYM { Lex->event_parse_data->status= Event_basic::ENABLED; @@ -1796,9 +1838,10 @@ opt_ev_status: /* empty */ { $$= 0; } Lex->event_parse_data->status= Event_basic::DISABLED; $$= 1; } - ; + ; -ev_starts: /* empty */ +ev_starts: + /* empty */ { Lex->event_parse_data->item_starts= new Item_func_now_local(); } @@ -1806,18 +1849,20 @@ ev_starts: /* empty */ { Lex->event_parse_data->item_starts= $2; } - ; + ; -ev_ends: /* empty */ +ev_ends: + /* empty */ | ENDS_SYM expr { Lex->event_parse_data->item_ends= $2; } - ; + ; -opt_ev_on_completion: /* empty */ { $$= 0; } +opt_ev_on_completion: + /* empty */ { $$= 0; } | ev_on_completion - ; + ; ev_on_completion: ON COMPLETION_SYM PRESERVE_SYM @@ -1832,15 +1877,16 @@ ev_on_completion: Event_basic::ON_COMPLETION_DROP; $$= 1; } - ; + ; -opt_ev_comment: /* empty */ { $$= 0; } +opt_ev_comment: + /* empty */ { $$= 0; } | COMMENT_SYM TEXT_STRING_sys { Lex->comment= Lex->event_parse_data->comment= $2; $$= 1; } - ; + ; ev_sql_stmt: { @@ -1876,7 +1922,7 @@ ev_sql_stmt: lex->sphead->reset_thd_mem_root(thd); lex->sphead->init(lex); - lex->sphead->init_sp_name(thd, lex->event_parse_data->identifier); + lex->sphead->init_sp_name(thd, lex->event_parse_data->identifier); lex->sphead->m_type= TYPE_ENUM_PROCEDURE; @@ -1898,7 +1944,7 @@ ev_sql_stmt: lex->event_parse_data->body_changed= TRUE; } - ; + ; ev_sql_stmt_inner: sp_proc_stmt_statement @@ -1912,60 +1958,60 @@ ev_sql_stmt_inner: | sp_proc_stmt_open | sp_proc_stmt_fetch | sp_proc_stmt_close - ; - + ; clear_privileges: - /* Nothing */ - { - LEX *lex=Lex; - lex->users_list.empty(); - lex->columns.empty(); - lex->grant= lex->grant_tot_col= 0; - lex->all_privileges= 0; - lex->select_lex.db= 0; - lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; - lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0; - bzero((char *)&(lex->mqh),sizeof(lex->mqh)); - } + /* Nothing */ + { + LEX *lex=Lex; + lex->users_list.empty(); + lex->columns.empty(); + lex->grant= lex->grant_tot_col= 0; + lex->all_privileges= 0; + lex->select_lex.db= 0; + lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; + lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0; + bzero((char *)&(lex->mqh),sizeof(lex->mqh)); + } ; sp_name: - ident '.' ident - { + ident '.' ident + { if (!$1.str || check_db_name(&$1)) { - my_error(ER_WRONG_DB_NAME, MYF(0), $1.str); - MYSQL_YYABORT; - } - if (check_routine_name(&$3)) - { - MYSQL_YYABORT; - } - $$= new sp_name($1, $3, true); - $$->init_qname(YYTHD); - } - | ident - { + my_error(ER_WRONG_DB_NAME, MYF(0), $1.str); + MYSQL_YYABORT; + } + if (check_routine_name(&$3)) + { + MYSQL_YYABORT; + } + $$= new sp_name($1, $3, true); + $$->init_qname(YYTHD); + } + | ident + { THD *thd= YYTHD; + LEX *lex= thd->lex; LEX_STRING db; - if (check_routine_name(&$1)) + if (check_routine_name(&$1)) { - MYSQL_YYABORT; - } - if (thd->copy_db_to(&db.str, &db.length)) MYSQL_YYABORT; - $$= new sp_name(db, $1, false); + } + if (lex->copy_db_to(&db.str, &db.length)) + MYSQL_YYABORT; + $$= new sp_name(db, $1, false); if ($$) - $$->init_qname(thd); - } - ; + $$->init_qname(thd); + } + ; create_function_tail: - RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys - { + RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys + { THD *thd= YYTHD; - LEX *lex= thd->lex; + LEX *lex= thd->lex; if (lex->definer != NULL) { /* @@ -1974,7 +2020,7 @@ create_function_tail: Using DEFINER with UDF has therefore no semantic, and is considered a parsing error. */ - my_error(ER_WRONG_USAGE, MYF(0), "SONAME", "DEFINER"); + my_error(ER_WRONG_USAGE, MYF(0), "SONAME", "DEFINER"); MYSQL_YYABORT; } if (is_native_function(thd, & lex->spname->m_name)) @@ -1983,17 +2029,17 @@ create_function_tail: lex->spname->m_name.str); MYSQL_YYABORT; } - lex->sql_command = SQLCOM_CREATE_FUNCTION; - lex->udf.name = lex->spname->m_name; - lex->udf.returns=(Item_result) $2; - lex->udf.dl=$4.str; - } - | '(' - { + lex->sql_command = SQLCOM_CREATE_FUNCTION; + lex->udf.name = lex->spname->m_name; + lex->udf.returns=(Item_result) $2; + lex->udf.dl=$4.str; + } + | '(' + { THD *thd= YYTHD; LEX *lex= thd->lex; Lex_input_stream *lip= thd->m_lip; - sp_head *sp; + sp_head *sp; const char* tmp_param_begin; /* @@ -2006,51 +2052,51 @@ create_function_tail: MYSQL_YYABORT; } - if (lex->sphead) - { - my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "FUNCTION"); - MYSQL_YYABORT; - } - /* Order is important here: new - reset - init */ - sp= new sp_head(); - sp->reset_thd_mem_root(thd); - sp->init(lex); + if (lex->sphead) + { + my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "FUNCTION"); + MYSQL_YYABORT; + } + /* Order is important here: new - reset - init */ + sp= new sp_head(); + sp->reset_thd_mem_root(thd); + sp->init(lex); sp->init_sp_name(thd, lex->spname); - sp->m_type= TYPE_ENUM_FUNCTION; - lex->sphead= sp; - /* - We have to turn off CLIENT_MULTI_QUERIES while parsing a - stored procedure, otherwise yylex will chop it into pieces - at each ';'. - */ + sp->m_type= TYPE_ENUM_FUNCTION; + lex->sphead= sp; + /* + We have to turn off CLIENT_MULTI_QUERIES while parsing a + stored procedure, otherwise yylex will chop it into pieces + at each ';'. + */ $<ulong_num>$= thd->client_capabilities & CLIENT_MULTI_QUERIES; - thd->client_capabilities &= ~CLIENT_MULTI_QUERIES; + thd->client_capabilities &= ~CLIENT_MULTI_QUERIES; tmp_param_begin= lip->get_cpp_tok_start(); tmp_param_begin++; lex->sphead->m_param_begin= tmp_param_begin; - } + } sp_fdparam_list ')' - { + { THD *thd= YYTHD; LEX *lex= thd->lex; Lex_input_stream *lip= thd->m_lip; lex->sphead->m_param_end= lip->get_cpp_tok_start(); - } - RETURNS_SYM - { - LEX *lex= Lex; - lex->charset= NULL; - lex->length= lex->dec= NULL; - lex->interval_list.empty(); - lex->type= 0; - } - type - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; + } + RETURNS_SYM + { + LEX *lex= Lex; + lex->charset= NULL; + lex->length= lex->dec= NULL; + lex->interval_list.empty(); + lex->type= 0; + } + type + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; /* This was disabled in 5.1.12. See bug #20701 When collation support in SP is implemented, then this test @@ -2068,28 +2114,28 @@ create_function_tail: &sp->m_return_field_def)) MYSQL_YYABORT; - bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); - } - sp_c_chistics - { + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + } + sp_c_chistics + { THD *thd= YYTHD; LEX *lex= thd->lex; Lex_input_stream *lip= thd->m_lip; - lex->sphead->m_chistics= &lex->sp_chistics; + lex->sphead->m_chistics= &lex->sp_chistics; lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); - } - sp_proc_stmt - { + } + sp_proc_stmt + { THD *thd= YYTHD; - LEX *lex= thd->lex; - sp_head *sp= lex->sphead; + LEX *lex= thd->lex; + sp_head *sp= lex->sphead; if (sp->is_not_allowed_in_function("function")) MYSQL_YYABORT; - lex->sql_command= SQLCOM_CREATE_SPFUNCTION; - sp->set_stmt_end(thd); + lex->sql_command= SQLCOM_CREATE_SPFUNCTION; + sp->set_stmt_end(thd); if (!(sp->m_flags & sp_head::HAS_RETURN)) { my_error(ER_SP_NORETURN, MYF(0), sp->m_qname.str); @@ -2130,76 +2176,76 @@ create_function_tail: ER(ER_NATIVE_FCT_NAME_COLLISION), sp->m_name.str); } - /* Restore flag if it was cleared above */ - thd->client_capabilities |= $<ulong_num>2; - sp->restore_thd_mem_root(thd); - } - ; + /* Restore flag if it was cleared above */ + thd->client_capabilities |= $<ulong_num>2; + sp->restore_thd_mem_root(thd); + } + ; sp_a_chistics: - /* Empty */ {} - | sp_a_chistics sp_chistic {} - ; + /* Empty */ {} + | sp_a_chistics sp_chistic {} + ; sp_c_chistics: - /* Empty */ {} - | sp_c_chistics sp_c_chistic {} - ; + /* Empty */ {} + | sp_c_chistics sp_c_chistic {} + ; /* Characteristics for both create and alter */ sp_chistic: - COMMENT_SYM TEXT_STRING_sys - { Lex->sp_chistics.comment= $2; } - | LANGUAGE_SYM SQL_SYM - { /* Just parse it, we only have one language for now. */ } - | NO_SYM SQL_SYM - { Lex->sp_chistics.daccess= SP_NO_SQL; } - | CONTAINS_SYM SQL_SYM - { Lex->sp_chistics.daccess= SP_CONTAINS_SQL; } - | READS_SYM SQL_SYM DATA_SYM - { Lex->sp_chistics.daccess= SP_READS_SQL_DATA; } - | MODIFIES_SYM SQL_SYM DATA_SYM - { Lex->sp_chistics.daccess= SP_MODIFIES_SQL_DATA; } - | sp_suid - { } - ; + COMMENT_SYM TEXT_STRING_sys + { Lex->sp_chistics.comment= $2; } + | LANGUAGE_SYM SQL_SYM + { /* Just parse it, we only have one language for now. */ } + | NO_SYM SQL_SYM + { Lex->sp_chistics.daccess= SP_NO_SQL; } + | CONTAINS_SYM SQL_SYM + { Lex->sp_chistics.daccess= SP_CONTAINS_SQL; } + | READS_SYM SQL_SYM DATA_SYM + { Lex->sp_chistics.daccess= SP_READS_SQL_DATA; } + | MODIFIES_SYM SQL_SYM DATA_SYM + { Lex->sp_chistics.daccess= SP_MODIFIES_SQL_DATA; } + | sp_suid + {} + ; /* Create characteristics */ sp_c_chistic: - sp_chistic { } - | DETERMINISTIC_SYM { Lex->sp_chistics.detistic= TRUE; } - | not DETERMINISTIC_SYM { Lex->sp_chistics.detistic= FALSE; } - ; + sp_chistic { } + | DETERMINISTIC_SYM { Lex->sp_chistics.detistic= TRUE; } + | not DETERMINISTIC_SYM { Lex->sp_chistics.detistic= FALSE; } + ; sp_suid: - SQL_SYM SECURITY_SYM DEFINER_SYM - { - Lex->sp_chistics.suid= SP_IS_SUID; - } - | SQL_SYM SECURITY_SYM INVOKER_SYM - { - Lex->sp_chistics.suid= SP_IS_NOT_SUID; - } - ; + SQL_SYM SECURITY_SYM DEFINER_SYM + { + Lex->sp_chistics.suid= SP_IS_SUID; + } + | SQL_SYM SECURITY_SYM INVOKER_SYM + { + Lex->sp_chistics.suid= SP_IS_NOT_SUID; + } + ; call: - CALL_SYM sp_name - { - LEX *lex = Lex; - - lex->sql_command= SQLCOM_CALL; - lex->spname= $2; - lex->value_list.empty(); - sp_add_used_routine(lex, YYTHD, $2, TYPE_ENUM_PROCEDURE); - } + CALL_SYM sp_name + { + LEX *lex = Lex; + + lex->sql_command= SQLCOM_CALL; + lex->spname= $2; + lex->value_list.empty(); + sp_add_used_routine(lex, YYTHD, $2, TYPE_ENUM_PROCEDURE); + } opt_sp_cparam_list {} - ; + ; /* CALL parameters */ opt_sp_cparam_list: - /* Empty */ - | '(' opt_sp_cparams ')' - ; + /* Empty */ + | '(' opt_sp_cparams ')' + ; opt_sp_cparams: /* Empty */ @@ -2207,58 +2253,58 @@ opt_sp_cparams: ; sp_cparams: - sp_cparams ',' expr - { - Lex->value_list.push_back($3); - } - | expr - { - Lex->value_list.push_back($1); - } - ; + sp_cparams ',' expr + { + Lex->value_list.push_back($3); + } + | expr + { + Lex->value_list.push_back($1); + } + ; /* Stored FUNCTION parameter declaration list */ sp_fdparam_list: - /* Empty */ - | sp_fdparams - ; + /* Empty */ + | sp_fdparams + ; sp_fdparams: - sp_fdparams ',' sp_fdparam - | sp_fdparam - ; + sp_fdparams ',' sp_fdparam + | sp_fdparam + ; sp_init_param: - /* Empty */ - { - LEX *lex= Lex; - - lex->length= 0; - lex->dec= 0; - lex->type= 0; - - lex->default_value= 0; - lex->on_update_value= 0; - - lex->comment= null_lex_str; - lex->charset= NULL; - - lex->interval_list.empty(); - lex->uint_geom_type= 0; - } - ; + /* Empty */ + { + LEX *lex= Lex; + + lex->length= 0; + lex->dec= 0; + lex->type= 0; + + lex->default_value= 0; + lex->on_update_value= 0; + + lex->comment= null_lex_str; + lex->charset= NULL; + + lex->interval_list.empty(); + lex->uint_geom_type= 0; + } + ; sp_fdparam: - ident sp_init_param type - { - LEX *lex= Lex; - sp_pcontext *spc= lex->spcont; - - if (spc->find_variable(&$1, TRUE)) - { - my_error(ER_SP_DUP_PARAM, MYF(0), $1.str); - MYSQL_YYABORT; - } + ident sp_init_param type + { + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_variable(&$1, TRUE)) + { + my_error(ER_SP_DUP_PARAM, MYF(0), $1.str); + MYSQL_YYABORT; + } sp_variable_t *spvar= spc->push_variable(&$1, (enum enum_field_types)$3, sp_param_in); @@ -2271,31 +2317,31 @@ sp_fdparam: } spvar->field_def.field_name= spvar->name.str; spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL; - } - ; + } + ; /* Stored PROCEDURE parameter declaration list */ sp_pdparam_list: - /* Empty */ - | sp_pdparams - ; + /* Empty */ + | sp_pdparams + ; sp_pdparams: - sp_pdparams ',' sp_pdparam - | sp_pdparam - ; + sp_pdparams ',' sp_pdparam + | sp_pdparam + ; sp_pdparam: - sp_opt_inout sp_init_param ident type - { - LEX *lex= Lex; - sp_pcontext *spc= lex->spcont; - - if (spc->find_variable(&$3, TRUE)) - { - my_error(ER_SP_DUP_PARAM, MYF(0), $3.str); - MYSQL_YYABORT; - } + sp_opt_inout sp_init_param ident type + { + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_variable(&$3, TRUE)) + { + my_error(ER_SP_DUP_PARAM, MYF(0), $3.str); + MYSQL_YYABORT; + } sp_variable_t *spvar= spc->push_variable(&$3, (enum enum_field_types)$4, (sp_param_mode_t)$1); @@ -2308,55 +2354,55 @@ sp_pdparam: } spvar->field_def.field_name= spvar->name.str; spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL; - } - ; + } + ; sp_opt_inout: - /* Empty */ { $$= sp_param_in; } - | IN_SYM { $$= sp_param_in; } - | OUT_SYM { $$= sp_param_out; } - | INOUT_SYM { $$= sp_param_inout; } - ; + /* Empty */ { $$= sp_param_in; } + | IN_SYM { $$= sp_param_in; } + | OUT_SYM { $$= sp_param_out; } + | INOUT_SYM { $$= sp_param_inout; } + ; sp_proc_stmts: - /* Empty */ {} - | sp_proc_stmts sp_proc_stmt ';' - ; + /* Empty */ {} + | sp_proc_stmts sp_proc_stmt ';' + ; sp_proc_stmts1: - sp_proc_stmt ';' {} - | sp_proc_stmts1 sp_proc_stmt ';' - ; + sp_proc_stmt ';' {} + | sp_proc_stmts1 sp_proc_stmt ';' + ; sp_decls: - /* Empty */ - { - $$.vars= $$.conds= $$.hndlrs= $$.curs= 0; - } - | sp_decls sp_decl ';' - { - /* We check for declarations out of (standard) order this way - because letting the grammar rules reflect it caused tricky - shift/reduce conflicts with the wrong result. (And we get - better error handling this way.) */ - if (($2.vars || $2.conds) && ($1.curs || $1.hndlrs)) - { /* Variable or condition following cursor or handler */ - my_message(ER_SP_VARCOND_AFTER_CURSHNDLR, + /* Empty */ + { + $$.vars= $$.conds= $$.hndlrs= $$.curs= 0; + } + | sp_decls sp_decl ';' + { + /* We check for declarations out of (standard) order this way + because letting the grammar rules reflect it caused tricky + shift/reduce conflicts with the wrong result. (And we get + better error handling this way.) */ + if (($2.vars || $2.conds) && ($1.curs || $1.hndlrs)) + { /* Variable or condition following cursor or handler */ + my_message(ER_SP_VARCOND_AFTER_CURSHNDLR, ER(ER_SP_VARCOND_AFTER_CURSHNDLR), MYF(0)); - MYSQL_YYABORT; - } - if ($2.curs && $1.hndlrs) - { /* Cursor following handler */ - my_message(ER_SP_CURSOR_AFTER_HANDLER, + 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)); - MYSQL_YYABORT; - } - $$.vars= $1.vars + $2.vars; - $$.conds= $1.conds + $2.conds; - $$.hndlrs= $1.hndlrs + $2.hndlrs; - $$.curs= $1.curs + $2.curs; - } - ; + MYSQL_YYABORT; + } + $$.vars= $1.vars + $2.vars; + $$.conds= $1.conds + $2.conds; + $$.hndlrs= $1.hndlrs + $2.hndlrs; + $$.curs= $1.curs + $2.curs; + } + ; sp_decl: DECLARE_SYM sp_decl_idents @@ -2415,126 +2461,126 @@ sp_decl: $$.vars= $2; $$.conds= $$.hndlrs= $$.curs= 0; } - | DECLARE_SYM ident CONDITION_SYM FOR_SYM sp_cond - { - LEX *lex= Lex; - sp_pcontext *spc= lex->spcont; - - if (spc->find_cond(&$2, TRUE)) - { - my_error(ER_SP_DUP_COND, MYF(0), $2.str); - MYSQL_YYABORT; - } - YYTHD->lex->spcont->push_cond(&$2, $5); - $$.vars= $$.hndlrs= $$.curs= 0; - $$.conds= 1; - } - | DECLARE_SYM sp_handler_type HANDLER_SYM FOR_SYM - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; + | DECLARE_SYM ident CONDITION_SYM FOR_SYM sp_cond + { + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_cond(&$2, TRUE)) + { + my_error(ER_SP_DUP_COND, MYF(0), $2.str); + MYSQL_YYABORT; + } + YYTHD->lex->spcont->push_cond(&$2, $5); + $$.vars= $$.hndlrs= $$.curs= 0; + $$.conds= 1; + } + | DECLARE_SYM sp_handler_type HANDLER_SYM FOR_SYM + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; lex->spcont= lex->spcont->push_context(LABEL_HANDLER_SCOPE); - sp_pcontext *ctx= lex->spcont; - sp_instr_hpush_jump *i= + sp_pcontext *ctx= lex->spcont; + sp_instr_hpush_jump *i= new sp_instr_hpush_jump(sp->instructions(), ctx, $2, - ctx->current_var_count()); - - sp->add_instr(i); - sp->push_backpatch(i, ctx->push_label((char *)"", 0)); - } - sp_hcond_list sp_proc_stmt - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - sp_pcontext *ctx= lex->spcont; - sp_label_t *hlab= lex->spcont->pop_label(); /* After this hdlr */ - sp_instr_hreturn *i; - - if ($2 == SP_HANDLER_CONTINUE) - { - i= new sp_instr_hreturn(sp->instructions(), ctx, - ctx->current_var_count()); - sp->add_instr(i); - } - else - { /* EXIT or UNDO handler, just jump to the end of the block */ - i= new sp_instr_hreturn(sp->instructions(), ctx, 0); - - sp->add_instr(i); - sp->push_backpatch(i, lex->spcont->last_label()); /* Block end */ - } - lex->sphead->backpatch(hlab); + ctx->current_var_count()); + + sp->add_instr(i); + sp->push_backpatch(i, ctx->push_label((char *)"", 0)); + } + sp_hcond_list sp_proc_stmt + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + sp_label_t *hlab= lex->spcont->pop_label(); /* After this hdlr */ + sp_instr_hreturn *i; + + if ($2 == SP_HANDLER_CONTINUE) + { + i= new sp_instr_hreturn(sp->instructions(), ctx, + ctx->current_var_count()); + sp->add_instr(i); + } + else + { /* EXIT or UNDO handler, just jump to the end of the block */ + i= new sp_instr_hreturn(sp->instructions(), ctx, 0); + + sp->add_instr(i); + sp->push_backpatch(i, lex->spcont->last_label()); /* Block end */ + } + lex->sphead->backpatch(hlab); lex->spcont= ctx->pop_context(); - $$.vars= $$.conds= $$.curs= 0; - $$.hndlrs= $6; - lex->spcont->add_handlers($6); - } - | DECLARE_SYM ident CURSOR_SYM FOR_SYM sp_cursor_stmt - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - sp_pcontext *ctx= lex->spcont; - uint offp; - sp_instr_cpush *i; - - if (ctx->find_cursor(&$2, &offp, TRUE)) - { - my_error(ER_SP_DUP_CURS, MYF(0), $2.str); - delete $5; - MYSQL_YYABORT; - } + $$.vars= $$.conds= $$.curs= 0; + $$.hndlrs= $6; + lex->spcont->add_handlers($6); + } + | DECLARE_SYM ident CURSOR_SYM FOR_SYM sp_cursor_stmt + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + uint offp; + sp_instr_cpush *i; + + if (ctx->find_cursor(&$2, &offp, TRUE)) + { + my_error(ER_SP_DUP_CURS, MYF(0), $2.str); + delete $5; + MYSQL_YYABORT; + } i= new sp_instr_cpush(sp->instructions(), ctx, $5, ctx->current_cursor_count()); - sp->add_instr(i); - ctx->push_cursor(&$2); - $$.vars= $$.conds= $$.hndlrs= 0; - $$.curs= 1; - } - ; + sp->add_instr(i); + ctx->push_cursor(&$2); + $$.vars= $$.conds= $$.hndlrs= 0; + $$.curs= 1; + } + ; sp_cursor_stmt: - { - Lex->sphead->reset_lex(YYTHD); - - /* - We use statement here just be able to get a better - error message. Using 'select' works too, but will then - result in a generic "syntax error" if a non-select - statement is given. - */ - } - statement - { - LEX *lex= Lex; - - if (lex->sql_command != SQLCOM_SELECT && - !(sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND)) - { - my_message(ER_SP_BAD_CURSOR_QUERY, ER(ER_SP_BAD_CURSOR_QUERY), + { + Lex->sphead->reset_lex(YYTHD); + + /* + We use statement here just be able to get a better + error message. Using 'select' works too, but will then + result in a generic "syntax error" if a non-select + statement is given. + */ + } + statement + { + LEX *lex= Lex; + + if (lex->sql_command != SQLCOM_SELECT && + !(sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND)) + { + my_message(ER_SP_BAD_CURSOR_QUERY, ER(ER_SP_BAD_CURSOR_QUERY), MYF(0)); - MYSQL_YYABORT; - } - if (lex->result) - { - my_message(ER_SP_BAD_CURSOR_SELECT, ER(ER_SP_BAD_CURSOR_SELECT), + MYSQL_YYABORT; + } + if (lex->result) + { + my_message(ER_SP_BAD_CURSOR_SELECT, ER(ER_SP_BAD_CURSOR_SELECT), MYF(0)); - MYSQL_YYABORT; - } - lex->sp_lex_in_use= TRUE; - $$= lex; - lex->sphead->restore_lex(YYTHD); - } - ; + MYSQL_YYABORT; + } + lex->sp_lex_in_use= TRUE; + $$= lex; + lex->sphead->restore_lex(YYTHD); + } + ; sp_handler_type: - EXIT_SYM { $$= SP_HANDLER_EXIT; } - | CONTINUE_SYM { $$= SP_HANDLER_CONTINUE; } -/* | UNDO_SYM { QQ No yet } */ - ; + EXIT_SYM { $$= SP_HANDLER_EXIT; } + | CONTINUE_SYM { $$= SP_HANDLER_CONTINUE; } + /*| UNDO_SYM { QQ No yet } */ + ; sp_hcond_list: sp_hcond_element @@ -2544,174 +2590,175 @@ sp_hcond_list: ; sp_hcond_element: - sp_hcond - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - sp_pcontext *ctx= lex->spcont->parent_context(); - - if (ctx->find_handler($1)) - { - my_message(ER_SP_DUP_HANDLER, ER(ER_SP_DUP_HANDLER), MYF(0)); - MYSQL_YYABORT; - } - else - { - sp_instr_hpush_jump *i= + sp_hcond + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont->parent_context(); + + if (ctx->find_handler($1)) + { + my_message(ER_SP_DUP_HANDLER, ER(ER_SP_DUP_HANDLER), MYF(0)); + MYSQL_YYABORT; + } + else + { + sp_instr_hpush_jump *i= (sp_instr_hpush_jump *)sp->last_instruction(); - i->add_condition($1); - ctx->push_handler($1); - } - } - ; + i->add_condition($1); + ctx->push_handler($1); + } + } + ; sp_cond: - ulong_num - { /* mysql errno */ - $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); - $$->type= sp_cond_type_t::number; - $$->mysqlerr= $1; - } - | SQLSTATE_SYM opt_value TEXT_STRING_literal - { /* SQLSTATE */ - if (!sp_cond_check(&$3)) - { - my_error(ER_SP_BAD_SQLSTATE, MYF(0), $3.str); - MYSQL_YYABORT; - } - $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); - $$->type= sp_cond_type_t::state; - memcpy($$->sqlstate, $3.str, 5); - $$->sqlstate[5]= '\0'; - } - ; + ulong_num + { /* mysql errno */ + $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$->type= sp_cond_type_t::number; + $$->mysqlerr= $1; + } + | SQLSTATE_SYM opt_value TEXT_STRING_literal + { /* SQLSTATE */ + if (!sp_cond_check(&$3)) + { + my_error(ER_SP_BAD_SQLSTATE, MYF(0), $3.str); + MYSQL_YYABORT; + } + $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$->type= sp_cond_type_t::state; + memcpy($$->sqlstate, $3.str, 5); + $$->sqlstate[5]= '\0'; + } + ; opt_value: - /* Empty */ {} - | VALUE_SYM {} - ; + /* Empty */ {} + | VALUE_SYM {} + ; sp_hcond: - sp_cond - { - $$= $1; - } - | ident /* CONDITION name */ - { - $$= Lex->spcont->find_cond(&$1); - if ($$ == NULL) - { - my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str); - MYSQL_YYABORT; - } - } - | SQLWARNING_SYM /* SQLSTATEs 01??? */ - { - $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); - $$->type= sp_cond_type_t::warning; - } - | not FOUND_SYM /* SQLSTATEs 02??? */ - { - $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); - $$->type= sp_cond_type_t::notfound; - } - | SQLEXCEPTION_SYM /* All other SQLSTATEs */ - { - $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); - $$->type= sp_cond_type_t::exception; - } - ; + sp_cond + { + $$= $1; + } + | ident /* CONDITION name */ + { + $$= Lex->spcont->find_cond(&$1); + if ($$ == NULL) + { + my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str); + MYSQL_YYABORT; + } + } + | SQLWARNING_SYM /* SQLSTATEs 01??? */ + { + $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$->type= sp_cond_type_t::warning; + } + | not FOUND_SYM /* SQLSTATEs 02??? */ + { + $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$->type= sp_cond_type_t::notfound; + } + | SQLEXCEPTION_SYM /* All other SQLSTATEs */ + { + $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$->type= sp_cond_type_t::exception; + } + ; sp_decl_idents: - ident - { + ident + { /* NOTE: field definition is filled in sp_decl section. */ - LEX *lex= Lex; - sp_pcontext *spc= lex->spcont; - - if (spc->find_variable(&$1, TRUE)) - { - my_error(ER_SP_DUP_VAR, MYF(0), $1.str); - MYSQL_YYABORT; - } - spc->push_variable(&$1, (enum_field_types)0, sp_param_in); - $$= 1; - } - | sp_decl_idents ',' ident - { + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_variable(&$1, TRUE)) + { + my_error(ER_SP_DUP_VAR, MYF(0), $1.str); + MYSQL_YYABORT; + } + spc->push_variable(&$1, (enum_field_types)0, sp_param_in); + $$= 1; + } + | sp_decl_idents ',' ident + { /* NOTE: field definition is filled in sp_decl section. */ - LEX *lex= Lex; - sp_pcontext *spc= lex->spcont; - - if (spc->find_variable(&$3, TRUE)) - { - my_error(ER_SP_DUP_VAR, MYF(0), $3.str); - MYSQL_YYABORT; - } - spc->push_variable(&$3, (enum_field_types)0, sp_param_in); - $$= $1 + 1; - } - ; + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_variable(&$3, TRUE)) + { + my_error(ER_SP_DUP_VAR, MYF(0), $3.str); + MYSQL_YYABORT; + } + spc->push_variable(&$3, (enum_field_types)0, sp_param_in); + $$= $1 + 1; + } + ; sp_opt_default: - /* Empty */ { $$ = NULL; } + /* Empty */ { $$ = NULL; } | DEFAULT expr { $$ = $2; } - ; + ; sp_proc_stmt: - sp_proc_stmt_statement + sp_proc_stmt_statement | sp_proc_stmt_return - | sp_proc_stmt_if - | case_stmt_specification - | sp_labeled_control - | sp_proc_stmt_unlabeled - | sp_proc_stmt_leave - | sp_proc_stmt_iterate - | sp_proc_stmt_open - | sp_proc_stmt_fetch + | sp_proc_stmt_if + | case_stmt_specification + | sp_labeled_control + | sp_proc_stmt_unlabeled + | sp_proc_stmt_leave + | sp_proc_stmt_iterate + | sp_proc_stmt_open + | sp_proc_stmt_fetch | sp_proc_stmt_close ; sp_proc_stmt_if: - IF { Lex->sphead->new_cont_backpatch(NULL); } - sp_if END IF - { Lex->sphead->do_cont_backpatch(); } + IF + { Lex->sphead->new_cont_backpatch(NULL); } + sp_if END IF + { Lex->sphead->do_cont_backpatch(); } ; sp_proc_stmt_statement: - { + { THD *thd= YYTHD; LEX *lex= thd->lex; Lex_input_stream *lip= thd->m_lip; - lex->sphead->reset_lex(thd); + lex->sphead->reset_lex(thd); lex->sphead->m_tmp_query= lip->get_tok_start(); - } - statement - { + } + statement + { THD *thd= YYTHD; LEX *lex= thd->lex; Lex_input_stream *lip= thd->m_lip; - sp_head *sp= lex->sphead; + sp_head *sp= lex->sphead; sp->m_flags|= sp_get_flags_for_command(lex); - if (lex->sql_command == SQLCOM_CHANGE_DB) - { /* "USE db" doesn't work in a procedure */ - my_error(ER_SP_BADSTATEMENT, MYF(0), "USE"); - MYSQL_YYABORT; - } - /* + if (lex->sql_command == SQLCOM_CHANGE_DB) + { /* "USE db" doesn't work in a procedure */ + my_error(ER_SP_BADSTATEMENT, MYF(0), "USE"); + MYSQL_YYABORT; + } + /* Don't add an instruction for SET statements, since all instructions for them were already added during processing of "set" rule. - */ + */ DBUG_ASSERT(lex->sql_command != SQLCOM_SET_OPTION || lex->var_list.is_empty()); if (lex->sql_command != SQLCOM_SET_OPTION) - { + { sp_instr_stmt *i=new sp_instr_stmt(sp->instructions(), lex->spcont, lex); @@ -2729,259 +2776,258 @@ sp_proc_stmt_statement: i->m_query.length); sp->add_instr(i); } - sp->restore_lex(thd); + sp->restore_lex(thd); } ; sp_proc_stmt_return: - RETURN_SYM + RETURN_SYM { Lex->sphead->reset_lex(YYTHD); } expr - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - - if (sp->m_type != TYPE_ENUM_FUNCTION) - { - my_message(ER_SP_BADRETURN, ER(ER_SP_BADRETURN), MYF(0)); - MYSQL_YYABORT; - } - else - { - sp_instr_freturn *i; - - i= new sp_instr_freturn(sp->instructions(), lex->spcont, $3, + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + + if (sp->m_type != TYPE_ENUM_FUNCTION) + { + my_message(ER_SP_BADRETURN, ER(ER_SP_BADRETURN), MYF(0)); + MYSQL_YYABORT; + } + else + { + sp_instr_freturn *i; + + i= new sp_instr_freturn(sp->instructions(), lex->spcont, $3, sp->m_return_field_def.sql_type, lex); - sp->add_instr(i); - sp->m_flags|= sp_head::HAS_RETURN; - } - sp->restore_lex(YYTHD); - } + sp->add_instr(i); + sp->m_flags|= sp_head::HAS_RETURN; + } + sp->restore_lex(YYTHD); + } ; sp_proc_stmt_unlabeled: - { /* Unlabeled controls get a secret label. */ - LEX *lex= Lex; + { /* Unlabeled controls get a secret label. */ + LEX *lex= Lex; - lex->spcont->push_label((char *)"", lex->sphead->instructions()); - } - sp_unlabeled_control - { - LEX *lex= Lex; + lex->spcont->push_label((char *)"", lex->sphead->instructions()); + } + sp_unlabeled_control + { + LEX *lex= Lex; - lex->sphead->backpatch(lex->spcont->pop_label()); - } + lex->sphead->backpatch(lex->spcont->pop_label()); + } ; sp_proc_stmt_leave: - LEAVE_SYM label_ident - { - LEX *lex= Lex; - sp_head *sp = lex->sphead; - sp_pcontext *ctx= lex->spcont; - sp_label_t *lab= ctx->find_label($2.str); - - if (! lab) - { - my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", $2.str); - MYSQL_YYABORT; - } - else - { - sp_instr_jump *i; - uint ip= sp->instructions(); - uint n; - - n= ctx->diff_handlers(lab->ctx, TRUE); /* Exclusive the dest. */ - if (n) - sp->add_instr(new sp_instr_hpop(ip++, ctx, n)); - n= ctx->diff_cursors(lab->ctx, TRUE); /* Exclusive the dest. */ - if (n) - sp->add_instr(new sp_instr_cpop(ip++, ctx, n)); - i= new sp_instr_jump(ip, ctx); - sp->push_backpatch(i, lab); /* Jumping forward */ + LEAVE_SYM label_ident + { + LEX *lex= Lex; + sp_head *sp = lex->sphead; + sp_pcontext *ctx= lex->spcont; + sp_label_t *lab= ctx->find_label($2.str); + + if (! lab) + { + my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", $2.str); + MYSQL_YYABORT; + } + else + { + sp_instr_jump *i; + uint ip= sp->instructions(); + uint n; + + n= ctx->diff_handlers(lab->ctx, TRUE); /* Exclusive the dest. */ + if (n) + sp->add_instr(new sp_instr_hpop(ip++, ctx, n)); + n= ctx->diff_cursors(lab->ctx, TRUE); /* Exclusive the dest. */ + if (n) + sp->add_instr(new sp_instr_cpop(ip++, ctx, n)); + i= new sp_instr_jump(ip, ctx); + sp->push_backpatch(i, lab); /* Jumping forward */ sp->add_instr(i); - } - } + } + } ; sp_proc_stmt_iterate: - ITERATE_SYM label_ident - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - sp_pcontext *ctx= lex->spcont; - sp_label_t *lab= ctx->find_label($2.str); - - if (! lab || lab->type != SP_LAB_ITER) - { - my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", $2.str); - MYSQL_YYABORT; - } - else - { - sp_instr_jump *i; - uint ip= sp->instructions(); - uint n; - - n= ctx->diff_handlers(lab->ctx, FALSE); /* Inclusive the dest. */ - if (n) - sp->add_instr(new sp_instr_hpop(ip++, ctx, n)); - n= ctx->diff_cursors(lab->ctx, FALSE); /* Inclusive the dest. */ - if (n) - sp->add_instr(new sp_instr_cpop(ip++, ctx, n)); - i= new sp_instr_jump(ip, ctx, lab->ip); /* Jump back */ + ITERATE_SYM label_ident + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + sp_label_t *lab= ctx->find_label($2.str); + + if (! lab || lab->type != SP_LAB_ITER) + { + my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", $2.str); + MYSQL_YYABORT; + } + else + { + sp_instr_jump *i; + uint ip= sp->instructions(); + uint n; + + n= ctx->diff_handlers(lab->ctx, FALSE); /* Inclusive the dest. */ + if (n) + sp->add_instr(new sp_instr_hpop(ip++, ctx, n)); + n= ctx->diff_cursors(lab->ctx, FALSE); /* Inclusive the dest. */ + if (n) + sp->add_instr(new sp_instr_cpop(ip++, ctx, n)); + i= new sp_instr_jump(ip, ctx, lab->ip); /* Jump back */ sp->add_instr(i); - } - } + } + } ; sp_proc_stmt_open: - OPEN_SYM ident - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - uint offset; - sp_instr_copen *i; - - if (! lex->spcont->find_cursor(&$2, &offset)) - { - my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str); - MYSQL_YYABORT; - } - i= new sp_instr_copen(sp->instructions(), lex->spcont, offset); - sp->add_instr(i); - } + OPEN_SYM ident + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + uint offset; + sp_instr_copen *i; + + if (! lex->spcont->find_cursor(&$2, &offset)) + { + my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str); + MYSQL_YYABORT; + } + i= new sp_instr_copen(sp->instructions(), lex->spcont, offset); + sp->add_instr(i); + } ; sp_proc_stmt_fetch: - FETCH_SYM sp_opt_fetch_noise ident INTO - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - uint offset; - sp_instr_cfetch *i; - - if (! lex->spcont->find_cursor(&$3, &offset)) - { - my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $3.str); - MYSQL_YYABORT; - } - i= new sp_instr_cfetch(sp->instructions(), lex->spcont, offset); - sp->add_instr(i); - } - sp_fetch_list - { } + FETCH_SYM sp_opt_fetch_noise ident INTO + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + uint offset; + sp_instr_cfetch *i; + + if (! lex->spcont->find_cursor(&$3, &offset)) + { + my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $3.str); + MYSQL_YYABORT; + } + i= new sp_instr_cfetch(sp->instructions(), lex->spcont, offset); + sp->add_instr(i); + } + sp_fetch_list + {} ; sp_proc_stmt_close: - CLOSE_SYM ident - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - uint offset; - sp_instr_cclose *i; - - if (! lex->spcont->find_cursor(&$2, &offset)) - { - my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str); - MYSQL_YYABORT; - } - i= new sp_instr_cclose(sp->instructions(), lex->spcont, offset); - sp->add_instr(i); - } - ; + CLOSE_SYM ident + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + uint offset; + sp_instr_cclose *i; + + if (! lex->spcont->find_cursor(&$2, &offset)) + { + my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str); + MYSQL_YYABORT; + } + i= new sp_instr_cclose(sp->instructions(), lex->spcont, offset); + sp->add_instr(i); + } + ; sp_opt_fetch_noise: - /* Empty */ - | NEXT_SYM FROM - | FROM - ; + /* Empty */ + | NEXT_SYM FROM + | FROM + ; sp_fetch_list: - ident - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - sp_pcontext *spc= lex->spcont; - sp_variable_t *spv; - - if (!spc || !(spv = spc->find_variable(&$1))) - { - my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str); - MYSQL_YYABORT; - } - else - { - /* An SP local variable */ - sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction(); - - i->add_to_varlist(spv); - } - } - | - sp_fetch_list ',' ident - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - sp_pcontext *spc= lex->spcont; - sp_variable_t *spv; - - if (!spc || !(spv = spc->find_variable(&$3))) - { - my_error(ER_SP_UNDECLARED_VAR, MYF(0), $3.str); - MYSQL_YYABORT; - } - else - { - /* An SP local variable */ - sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction(); - - i->add_to_varlist(spv); - } - } - ; + ident + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *spc= lex->spcont; + sp_variable_t *spv; + + if (!spc || !(spv = spc->find_variable(&$1))) + { + my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str); + MYSQL_YYABORT; + } + else + { + /* An SP local variable */ + sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction(); + + i->add_to_varlist(spv); + } + } + | sp_fetch_list ',' ident + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *spc= lex->spcont; + sp_variable_t *spv; + + if (!spc || !(spv = spc->find_variable(&$3))) + { + my_error(ER_SP_UNDECLARED_VAR, MYF(0), $3.str); + MYSQL_YYABORT; + } + else + { + /* An SP local variable */ + sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction(); + + i->add_to_varlist(spv); + } + } + ; sp_if: { 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 = new sp_instr_jump_if_not(ip, ctx, + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + uint ip= sp->instructions(); + sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, ctx, $2, lex); - sp->push_backpatch(i, ctx->push_label((char *)"", 0)); + 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); - - sp->add_instr(i); - sp->backpatch(ctx->pop_label()); - sp->push_backpatch(i, ctx->push_label((char *)"", 0)); - } - sp_elseifs - { - LEX *lex= Lex; - - lex->sphead->backpatch(lex->spcont->pop_label()); - } - ; + } + 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); + + sp->add_instr(i); + sp->backpatch(ctx->pop_label()); + sp->push_backpatch(i, ctx->push_label((char *)"", 0)); + } + sp_elseifs + { + LEX *lex= Lex; + + lex->sphead->backpatch(lex->spcont->pop_label()); + } + ; sp_elseifs: - /* Empty */ - | ELSEIF_SYM sp_if - | ELSE sp_proc_stmts1 - ; + /* Empty */ + | ELSEIF_SYM sp_if + | ELSE sp_proc_stmts1 + ; case_stmt_specification: simple_case_stmt @@ -3093,138 +3139,138 @@ else_clause_opt: ; sp_labeled_control: - label_ident ':' - { - LEX *lex= Lex; - sp_pcontext *ctx= lex->spcont; - sp_label_t *lab= ctx->find_label($1.str); - - if (lab) - { - my_error(ER_SP_LABEL_REDEFINE, MYF(0), $1.str); - MYSQL_YYABORT; - } - else - { - lab= lex->spcont->push_label($1.str, - lex->sphead->instructions()); - lab->type= SP_LAB_ITER; - } - } - sp_unlabeled_control sp_opt_label - { - LEX *lex= Lex; - - if ($5.str) - { - sp_label_t *lab= lex->spcont->find_label($5.str); - - if (!lab || - my_strcasecmp(system_charset_info, $5.str, lab->name) != 0) - { - my_error(ER_SP_LABEL_MISMATCH, MYF(0), $5.str); - MYSQL_YYABORT; - } - } - lex->sphead->backpatch(lex->spcont->pop_label()); - } - ; + label_ident ':' + { + LEX *lex= Lex; + sp_pcontext *ctx= lex->spcont; + sp_label_t *lab= ctx->find_label($1.str); + + if (lab) + { + my_error(ER_SP_LABEL_REDEFINE, MYF(0), $1.str); + MYSQL_YYABORT; + } + else + { + lab= lex->spcont->push_label($1.str, + lex->sphead->instructions()); + lab->type= SP_LAB_ITER; + } + } + sp_unlabeled_control sp_opt_label + { + LEX *lex= Lex; + + if ($5.str) + { + sp_label_t *lab= lex->spcont->find_label($5.str); + + if (!lab || + my_strcasecmp(system_charset_info, $5.str, lab->name) != 0) + { + my_error(ER_SP_LABEL_MISMATCH, MYF(0), $5.str); + MYSQL_YYABORT; + } + } + lex->sphead->backpatch(lex->spcont->pop_label()); + } + ; sp_opt_label: - /* Empty */ { $$= null_lex_str; } + /* Empty */ { $$= null_lex_str; } | label_ident { $$= $1; } - ; + ; sp_unlabeled_control: - BEGIN_SYM - { /* QQ This is just a dummy for grouping declarations and statements - together. No [[NOT] ATOMIC] yet, and we need to figure out how - make it coexist with the existing BEGIN COMMIT/ROLLBACK. */ - LEX *lex= Lex; - sp_label_t *lab= lex->spcont->last_label(); - - lab->type= SP_LAB_BEGIN; - lex->spcont= lex->spcont->push_context(LABEL_DEFAULT_SCOPE); - } - sp_decls - sp_proc_stmts - END - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - sp_pcontext *ctx= lex->spcont; - - sp->backpatch(ctx->last_label()); /* We always have a label */ - if ($3.hndlrs) - sp->add_instr(new sp_instr_hpop(sp->instructions(), ctx, - $3.hndlrs)); - if ($3.curs) - sp->add_instr(new sp_instr_cpop(sp->instructions(), ctx, - $3.curs)); - lex->spcont= ctx->pop_context(); - } - | LOOP_SYM - sp_proc_stmts1 END LOOP_SYM - { - LEX *lex= Lex; - uint ip= lex->sphead->instructions(); - sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */ - sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip); - - lex->sphead->add_instr(i); - } + BEGIN_SYM + { /* QQ This is just a dummy for grouping declarations and statements + together. No [[NOT] ATOMIC] yet, and we need to figure out how + make it coexist with the existing BEGIN COMMIT/ROLLBACK. */ + LEX *lex= Lex; + sp_label_t *lab= lex->spcont->last_label(); + + lab->type= SP_LAB_BEGIN; + lex->spcont= lex->spcont->push_context(LABEL_DEFAULT_SCOPE); + } + sp_decls + sp_proc_stmts + END + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + + sp->backpatch(ctx->last_label()); /* We always have a label */ + if ($3.hndlrs) + sp->add_instr(new sp_instr_hpop(sp->instructions(), ctx, + $3.hndlrs)); + if ($3.curs) + sp->add_instr(new sp_instr_cpop(sp->instructions(), ctx, + $3.curs)); + lex->spcont= ctx->pop_context(); + } + | LOOP_SYM + sp_proc_stmts1 END LOOP_SYM + { + LEX *lex= Lex; + uint ip= lex->sphead->instructions(); + sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */ + sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip); + + lex->sphead->add_instr(i); + } | WHILE_SYM { Lex->sphead->reset_lex(YYTHD); } expr DO_SYM - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - uint ip= sp->instructions(); - sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont, - $3, lex); - - /* Jumping forward */ - sp->push_backpatch(i, lex->spcont->last_label()); + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + uint ip= sp->instructions(); + sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont, + $3, lex); + + /* Jumping forward */ + sp->push_backpatch(i, lex->spcont->last_label()); sp->new_cont_backpatch(i); sp->add_instr(i); sp->restore_lex(YYTHD); - } - sp_proc_stmts1 END WHILE_SYM - { - LEX *lex= Lex; - uint ip= lex->sphead->instructions(); - sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */ - sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip); - - lex->sphead->add_instr(i); + } + sp_proc_stmts1 END WHILE_SYM + { + LEX *lex= Lex; + uint ip= lex->sphead->instructions(); + sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */ + sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip); + + lex->sphead->add_instr(i); lex->sphead->do_cont_backpatch(); - } + } | REPEAT_SYM sp_proc_stmts1 UNTIL_SYM { Lex->sphead->reset_lex(YYTHD); } expr END REPEAT_SYM - { - LEX *lex= Lex; - uint ip= lex->sphead->instructions(); - sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */ - sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont, + { + LEX *lex= Lex; + uint ip= lex->sphead->instructions(); + sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */ + sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont, $5, lab->ip, lex); lex->sphead->add_instr(i); lex->sphead->restore_lex(YYTHD); /* We can shortcut the cont_backpatch here */ i->m_cont_dest= ip+1; - } - ; + } + ; trg_action_time: - BEFORE_SYM + BEFORE_SYM { Lex->trg_chistics.action_time= TRG_ACTION_BEFORE; } - | AFTER_SYM + | AFTER_SYM { Lex->trg_chistics.action_time= TRG_ACTION_AFTER; } ; trg_event: - INSERT + INSERT { Lex->trg_chistics.event= TRG_EVENT_INSERT; } | UPDATE_SYM { Lex->trg_chistics.event= TRG_EVENT_UPDATE; } @@ -3247,156 +3293,157 @@ trg_event: change_tablespace_access: tablespace_name ts_access_mode - ; + ; change_tablespace_info: tablespace_name CHANGE ts_datafile change_ts_option_list - ; + ; tablespace_info: tablespace_name ADD ts_datafile opt_logfile_group_name tablespace_option_list - ; + ; opt_logfile_group_name: /* empty */ {} - | USE_SYM LOGFILE_SYM GROUP_SYM ident + | USE_SYM LOGFILE_SYM GROUP_SYM ident { LEX *lex= Lex; lex->alter_tablespace_info->logfile_group_name= $4.str; - }; + } + ; alter_tablespace_info: tablespace_name ADD ts_datafile - alter_tablespace_option_list - { - Lex->alter_tablespace_info->ts_alter_tablespace_type= ALTER_TABLESPACE_ADD_FILE; + alter_tablespace_option_list + { + Lex->alter_tablespace_info->ts_alter_tablespace_type= ALTER_TABLESPACE_ADD_FILE; } - | - tablespace_name + | tablespace_name DROP ts_datafile - alter_tablespace_option_list - { - Lex->alter_tablespace_info->ts_alter_tablespace_type= ALTER_TABLESPACE_DROP_FILE; - }; + alter_tablespace_option_list + { + Lex->alter_tablespace_info->ts_alter_tablespace_type= ALTER_TABLESPACE_DROP_FILE; + } + ; logfile_group_info: logfile_group_name add_log_file logfile_group_option_list - ; + ; alter_logfile_group_info: logfile_group_name add_log_file alter_logfile_group_option_list - ; + ; add_log_file: ADD lg_undofile - | ADD lg_redofile - ; + | ADD lg_redofile + ; change_ts_option_list: /* empty */ {} change_ts_options - ; + ; change_ts_options: change_ts_option - | change_ts_options change_ts_option - | change_ts_options ',' change_ts_option - ; + | change_ts_options change_ts_option + | change_ts_options ',' change_ts_option + ; change_ts_option: opt_ts_initial_size - | opt_ts_autoextend_size - | opt_ts_max_size - ; + | opt_ts_autoextend_size + | opt_ts_max_size + ; tablespace_option_list: /* empty */ {} - tablespace_options - ; + tablespace_options + ; tablespace_options: tablespace_option - | tablespace_options tablespace_option - | tablespace_options ',' tablespace_option - ; + | tablespace_options tablespace_option + | tablespace_options ',' tablespace_option + ; tablespace_option: opt_ts_initial_size - | opt_ts_autoextend_size - | opt_ts_max_size - | opt_ts_extent_size - | opt_ts_nodegroup - | opt_ts_engine - | ts_wait - | opt_ts_comment - ; + | opt_ts_autoextend_size + | opt_ts_max_size + | opt_ts_extent_size + | opt_ts_nodegroup + | opt_ts_engine + | ts_wait + | opt_ts_comment + ; alter_tablespace_option_list: /* empty */ {} - alter_tablespace_options - ; + alter_tablespace_options + ; alter_tablespace_options: alter_tablespace_option - | alter_tablespace_options alter_tablespace_option - | alter_tablespace_options ',' alter_tablespace_option - ; + | alter_tablespace_options alter_tablespace_option + | alter_tablespace_options ',' alter_tablespace_option + ; alter_tablespace_option: opt_ts_initial_size - | opt_ts_autoextend_size - | opt_ts_max_size - | opt_ts_engine - | ts_wait - ; + | opt_ts_autoextend_size + | opt_ts_max_size + | opt_ts_engine + | ts_wait + ; logfile_group_option_list: /* empty */ {} - logfile_group_options - ; + logfile_group_options + ; logfile_group_options: logfile_group_option - | logfile_group_options logfile_group_option - | logfile_group_options ',' logfile_group_option - ; + | logfile_group_options logfile_group_option + | logfile_group_options ',' logfile_group_option + ; logfile_group_option: opt_ts_initial_size - | opt_ts_undo_buffer_size - | opt_ts_redo_buffer_size - | opt_ts_nodegroup - | opt_ts_engine - | ts_wait - | opt_ts_comment - ; + | opt_ts_undo_buffer_size + | opt_ts_redo_buffer_size + | opt_ts_nodegroup + | opt_ts_engine + | ts_wait + | opt_ts_comment + ; alter_logfile_group_option_list: /* empty */ {} alter_logfile_group_options - ; + ; alter_logfile_group_options: alter_logfile_group_option - | alter_logfile_group_options alter_logfile_group_option - | alter_logfile_group_options ',' alter_logfile_group_option - ; + | alter_logfile_group_options alter_logfile_group_option + | alter_logfile_group_options ',' alter_logfile_group_option + ; alter_logfile_group_option: opt_ts_initial_size - | opt_ts_engine - | ts_wait - ; + | opt_ts_engine + | ts_wait + ; ts_datafile: @@ -3404,21 +3451,24 @@ ts_datafile: { LEX *lex= Lex; lex->alter_tablespace_info->data_file_name= $2.str; - }; + } + ; lg_undofile: UNDOFILE_SYM TEXT_STRING_sys { LEX *lex= Lex; lex->alter_tablespace_info->undo_file_name= $2.str; - }; + } + ; lg_redofile: REDOFILE_SYM TEXT_STRING_sys { LEX *lex= Lex; lex->alter_tablespace_info->redo_file_name= $2.str; - }; + } + ; tablespace_name: ident @@ -3427,7 +3477,8 @@ tablespace_name: lex->alter_tablespace_info= new st_alter_tablespace(); lex->alter_tablespace_info->tablespace_name= $1.str; lex->sql_command= SQLCOM_ALTER_TABLESPACE; - }; + } + ; logfile_group_name: ident @@ -3436,7 +3487,8 @@ logfile_group_name: lex->alter_tablespace_info= new st_alter_tablespace(); lex->alter_tablespace_info->logfile_group_name= $1.str; lex->sql_command= SQLCOM_ALTER_TABLESPACE; - }; + } + ; ts_access_mode: READ_ONLY_SYM @@ -3444,58 +3496,65 @@ ts_access_mode: LEX *lex= Lex; lex->alter_tablespace_info->ts_access_mode= TS_READ_ONLY; } - | READ_WRITE_SYM + | READ_WRITE_SYM { LEX *lex= Lex; lex->alter_tablespace_info->ts_access_mode= TS_READ_WRITE; } - | NOT_SYM ACCESSIBLE_SYM + | NOT_SYM ACCESSIBLE_SYM { LEX *lex= Lex; lex->alter_tablespace_info->ts_access_mode= TS_NOT_ACCESSIBLE; - }; + } + ; opt_ts_initial_size: INITIAL_SIZE_SYM opt_equal size_number { LEX *lex= Lex; lex->alter_tablespace_info->initial_size= $3; - }; + } + ; opt_ts_autoextend_size: AUTOEXTEND_SIZE_SYM opt_equal size_number { LEX *lex= Lex; lex->alter_tablespace_info->autoextend_size= $3; - }; + } + ; opt_ts_max_size: MAX_SIZE_SYM opt_equal size_number { LEX *lex= Lex; lex->alter_tablespace_info->max_size= $3; - }; + } + ; opt_ts_extent_size: EXTENT_SIZE_SYM opt_equal size_number { LEX *lex= Lex; lex->alter_tablespace_info->extent_size= $3; - }; + } + ; opt_ts_undo_buffer_size: UNDO_BUFFER_SIZE_SYM opt_equal size_number { LEX *lex= Lex; lex->alter_tablespace_info->undo_buffer_size= $3; - }; + } + ; opt_ts_redo_buffer_size: REDO_BUFFER_SIZE_SYM opt_equal size_number { LEX *lex= Lex; lex->alter_tablespace_info->redo_buffer_size= $3; - }; + } + ; opt_ts_nodegroup: NODEGROUP_SYM opt_equal real_ulong_num @@ -3507,7 +3566,8 @@ opt_ts_nodegroup: MYSQL_YYABORT; } lex->alter_tablespace_info->nodegroup_id= $3; - }; + } + ; opt_ts_comment: COMMENT_SYM opt_equal TEXT_STRING_sys @@ -3519,7 +3579,8 @@ opt_ts_comment: MYSQL_YYABORT; } lex->alter_tablespace_info->ts_comment= $3.str; - }; + } + ; opt_ts_engine: opt_storage ENGINE_SYM opt_equal storage_engines @@ -3532,12 +3593,13 @@ opt_ts_engine: MYSQL_YYABORT; } lex->alter_tablespace_info->storage_engine= $4; - }; + } + ; opt_ts_wait: /* empty */ - | ts_wait - ; + | ts_wait + ; ts_wait: WAIT_SYM @@ -3545,7 +3607,7 @@ ts_wait: LEX *lex= Lex; lex->alter_tablespace_info->wait_until_completed= TRUE; } - | NO_WAIT_SYM + | NO_WAIT_SYM { LEX *lex= Lex; if (!(lex->alter_tablespace_info->wait_until_completed)) @@ -3554,11 +3616,12 @@ ts_wait: MYSQL_YYABORT; } lex->alter_tablespace_info->wait_until_completed= FALSE; - }; + } + ; size_number: real_ulong_num { $$= $1;} - | IDENT + | IDENT { ulonglong number; uint text_shift_number= 0; @@ -3602,16 +3665,16 @@ size_number: } $$= number; } - ; + ; /* End tablespace part */ create2: - '(' create2a {} + '(' create2a {} | opt_create_table_options - opt_partitioning {} + opt_partitioning {} create3 {} | LIKE table_ident { @@ -3634,7 +3697,7 @@ create2: ; create2a: - field_list ')' opt_create_table_options + field_list ')' opt_create_table_options opt_partitioning {} create3 {} | opt_partitioning {} @@ -3643,10 +3706,10 @@ create2a: ; create3: - /* empty */ {} - | opt_duplicate opt_as create_select + /* empty */ {} + | opt_duplicate opt_as create_select { Select->set_braces(0);} union_clause {} - | opt_duplicate opt_as '(' create_select ')' + | opt_duplicate opt_as '(' create_select ')' { Select->set_braces(1);} union_opt {} ; @@ -3675,553 +3738,552 @@ create3: from the openfrm function. */ opt_partitioning: - /* empty */ {} + /* empty */ {} | partitioning ; partitioning: - PARTITION_SYM - { -#ifdef WITH_PARTITION_STORAGE_ENGINE - LEX *lex= Lex; - LEX_STRING partition_name={C_STRING_WITH_LEN("partition")}; - if (!plugin_is_ready(&partition_name, MYSQL_STORAGE_ENGINE_PLUGIN)) + PARTITION_SYM { +#ifdef WITH_PARTITION_STORAGE_ENGINE + LEX *lex= Lex; + LEX_STRING partition_name={C_STRING_WITH_LEN("partition")}; + if (!plugin_is_ready(&partition_name, MYSQL_STORAGE_ENGINE_PLUGIN)) + { + my_error(ER_FEATURE_DISABLED, MYF(0), + "partitioning", "--with-partition"); + MYSQL_YYABORT; + } + lex->part_info= new partition_info(); + if (!lex->part_info) + { + mem_alloc_error(sizeof(partition_info)); + 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; - } - lex->part_info= new partition_info(); - if (!lex->part_info) - { - mem_alloc_error(sizeof(partition_info)); - 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 + } + partition ; partition_entry: - PARTITION_SYM - { - LEX *lex= Lex; - if (!lex->part_info) + PARTITION_SYM { - my_parse_error(ER(ER_PARTITION_ENTRY_ERROR)); - MYSQL_YYABORT; + LEX *lex= Lex; + if (!lex->part_info) + { + my_parse_error(ER(ER_PARTITION_ENTRY_ERROR)); + MYSQL_YYABORT; + } + /* + We enter here when opening the frm file to translate + partition info string into part_info data structure. + */ } - /* - We enter here when opening the frm file to translate - partition info string into part_info data structure. - */ - } - partition {} + partition {} ; partition: - BY part_type_def opt_no_parts {} opt_sub_part {} part_defs + BY part_type_def opt_no_parts {} opt_sub_part {} part_defs ; part_type_def: - opt_linear KEY_SYM '(' part_field_list ')' - { - LEX *lex= Lex; - lex->part_info->list_of_part_fields= TRUE; - lex->part_info->part_type= HASH_PARTITION; - } + opt_linear KEY_SYM '(' part_field_list ')' + { + LEX *lex= Lex; + lex->part_info->list_of_part_fields= TRUE; + lex->part_info->part_type= HASH_PARTITION; + } | opt_linear HASH_SYM - { Lex->part_info->part_type= HASH_PARTITION; } - part_func {} + { Lex->part_info->part_type= HASH_PARTITION; } + part_func {} | RANGE_SYM - { Lex->part_info->part_type= RANGE_PARTITION; } - part_func {} + { Lex->part_info->part_type= RANGE_PARTITION; } + part_func {} | LIST_SYM - { Lex->part_info->part_type= LIST_PARTITION; } - part_func {} + { Lex->part_info->part_type= LIST_PARTITION; } + part_func {} ; opt_linear: - /* empty */ {} + /* empty */ {} | LINEAR_SYM - { Lex->part_info->linear_hash_ind= TRUE;} + { Lex->part_info->linear_hash_ind= TRUE;} ; part_field_list: - /* empty */ {} + /* empty */ {} | part_field_item_list {} ; part_field_item_list: - part_field_item {} + part_field_item {} | part_field_item_list ',' part_field_item {} ; part_field_item: - ident - { - if (Lex->part_info->part_field_list.push_back($1.str)) + ident { - mem_alloc_error(1); - MYSQL_YYABORT; + if (Lex->part_info->part_field_list.push_back($1.str)) + { + mem_alloc_error(1); + MYSQL_YYABORT; + } } - } ; part_func: - '(' remember_name part_func_expr remember_end ')' - { - LEX *lex= Lex; - uint expr_len= (uint)($4 - $2) - 1; - lex->part_info->list_of_part_fields= FALSE; - lex->part_info->part_expr= $3; - lex->part_info->part_func_string= (char* ) sql_memdup($2+1, expr_len); - lex->part_info->part_func_len= expr_len; - } + '(' remember_name part_func_expr remember_end ')' + { + LEX *lex= Lex; + uint expr_len= (uint)($4 - $2) - 1; + lex->part_info->list_of_part_fields= FALSE; + lex->part_info->part_expr= $3; + lex->part_info->part_func_string= (char* ) sql_memdup($2+1, expr_len); + lex->part_info->part_func_len= expr_len; + } ; sub_part_func: - '(' remember_name part_func_expr remember_end ')' - { - LEX *lex= Lex; - uint expr_len= (uint)($4 - $2) - 1; - lex->part_info->list_of_subpart_fields= FALSE; - lex->part_info->subpart_expr= $3; - lex->part_info->subpart_func_string= (char* ) sql_memdup($2+1, expr_len); - lex->part_info->subpart_func_len= expr_len; - } + '(' remember_name part_func_expr remember_end ')' + { + LEX *lex= Lex; + uint expr_len= (uint)($4 - $2) - 1; + lex->part_info->list_of_subpart_fields= FALSE; + lex->part_info->subpart_expr= $3; + lex->part_info->subpart_func_string= (char* ) sql_memdup($2+1, expr_len); + lex->part_info->subpart_func_len= expr_len; + } ; opt_no_parts: - /* empty */ {} - | PARTITIONS_SYM real_ulong_num - { - uint no_parts= $2; - LEX *lex= Lex; - if (no_parts == 0) - { - my_error(ER_NO_PARTS_ERROR, MYF(0), "partitions"); - MYSQL_YYABORT; - } + /* empty */ {} + | PARTITIONS_SYM real_ulong_num + { + uint no_parts= $2; + LEX *lex= Lex; + if (no_parts == 0) + { + my_error(ER_NO_PARTS_ERROR, MYF(0), "partitions"); + MYSQL_YYABORT; + } - lex->part_info->no_parts= no_parts; - lex->part_info->use_default_no_partitions= FALSE; - } + lex->part_info->no_parts= no_parts; + lex->part_info->use_default_no_partitions= FALSE; + } ; opt_sub_part: - /* empty */ {} + /* empty */ {} | SUBPARTITION_SYM BY opt_linear HASH_SYM sub_part_func - { Lex->part_info->subpart_type= HASH_PARTITION; } - opt_no_subparts {} + { Lex->part_info->subpart_type= HASH_PARTITION; } + opt_no_subparts {} | SUBPARTITION_SYM BY opt_linear KEY_SYM '(' sub_part_field_list ')' - { - LEX *lex= Lex; - lex->part_info->subpart_type= HASH_PARTITION; - lex->part_info->list_of_subpart_fields= TRUE; - } - opt_no_subparts {} + { + LEX *lex= Lex; + lex->part_info->subpart_type= HASH_PARTITION; + lex->part_info->list_of_subpart_fields= TRUE; + } + opt_no_subparts {} ; sub_part_field_list: - sub_part_field_item {} + sub_part_field_item {} | sub_part_field_list ',' sub_part_field_item {} ; sub_part_field_item: - ident - { - if (Lex->part_info->subpart_field_list.push_back($1.str)) + ident { - mem_alloc_error(1); - MYSQL_YYABORT; + if (Lex->part_info->subpart_field_list.push_back($1.str)) + { + mem_alloc_error(1); + MYSQL_YYABORT; + } } - } ; part_func_expr: - bit_expr - { - LEX *lex= Lex; - bool not_corr_func; - not_corr_func= !lex->safe_to_cache_query; - lex->safe_to_cache_query= 1; - if (not_corr_func) + bit_expr { - my_parse_error(ER(ER_CONST_EXPR_IN_PARTITION_FUNC_ERROR)); - MYSQL_YYABORT; + LEX *lex= Lex; + bool not_corr_func; + not_corr_func= !lex->safe_to_cache_query; + lex->safe_to_cache_query= 1; + if (not_corr_func) + { + my_parse_error(ER(ER_CONST_EXPR_IN_PARTITION_FUNC_ERROR)); + MYSQL_YYABORT; + } + $$=$1; } - $$=$1; - } ; opt_no_subparts: - /* empty */ {} + /* empty */ {} | SUBPARTITIONS_SYM real_ulong_num - { - uint no_parts= $2; - LEX *lex= Lex; - if (no_parts == 0) { - my_error(ER_NO_PARTS_ERROR, MYF(0), "subpartitions"); - MYSQL_YYABORT; + uint no_parts= $2; + LEX *lex= Lex; + if (no_parts == 0) + { + my_error(ER_NO_PARTS_ERROR, MYF(0), "subpartitions"); + MYSQL_YYABORT; + } + lex->part_info->no_subparts= no_parts; + lex->part_info->use_default_no_subpartitions= FALSE; } - lex->part_info->no_subparts= no_parts; - lex->part_info->use_default_no_subpartitions= FALSE; - } ; part_defs: - /* empty */ - {} + /* empty */ + {} | '(' part_def_list ')' - { - LEX *lex= Lex; - partition_info *part_info= lex->part_info; - uint count_curr_parts= part_info->partitions.elements; - if (part_info->no_parts != 0) { - if (part_info->no_parts != - count_curr_parts) + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + uint count_curr_parts= part_info->partitions.elements; + if (part_info->no_parts != 0) { - my_parse_error(ER(ER_PARTITION_WRONG_NO_PART_ERROR)); - MYSQL_YYABORT; + if (part_info->no_parts != + count_curr_parts) + { + my_parse_error(ER(ER_PARTITION_WRONG_NO_PART_ERROR)); + MYSQL_YYABORT; + } } + else if (count_curr_parts > 0) + { + part_info->no_parts= count_curr_parts; + } + part_info->count_curr_subparts= 0; } - else if (count_curr_parts > 0) - { - part_info->no_parts= count_curr_parts; - } - part_info->count_curr_subparts= 0; - } ; part_def_list: - part_definition {} + part_definition {} | part_def_list ',' part_definition {} ; part_definition: - PARTITION_SYM - { - LEX *lex= Lex; - partition_info *part_info= lex->part_info; - partition_element *p_elem= new partition_element(); - - if (!p_elem || part_info->partitions.push_back(p_elem)) + PARTITION_SYM { - mem_alloc_error(sizeof(partition_element)); - MYSQL_YYABORT; + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + partition_element *p_elem= new partition_element(); + + if (!p_elem || part_info->partitions.push_back(p_elem)) + { + mem_alloc_error(sizeof(partition_element)); + MYSQL_YYABORT; + } + p_elem->part_state= PART_NORMAL; + part_info->curr_part_elem= p_elem; + part_info->current_partition= p_elem; + part_info->use_default_partitions= FALSE; + part_info->use_default_no_partitions= FALSE; } - p_elem->part_state= PART_NORMAL; - part_info->curr_part_elem= p_elem; - part_info->current_partition= p_elem; - part_info->use_default_partitions= FALSE; - part_info->use_default_no_partitions= FALSE; - } - part_name {} - opt_part_values {} - opt_part_options {} - opt_sub_partition {} + part_name {} + opt_part_values {} + opt_part_options {} + opt_sub_partition {} ; part_name: - ident - { - LEX *lex= Lex; - partition_info *part_info= lex->part_info; - partition_element *p_elem= part_info->curr_part_elem; - p_elem->partition_name= $1.str; - } + ident + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + partition_element *p_elem= part_info->curr_part_elem; + p_elem->partition_name= $1.str; + } ; opt_part_values: - /* empty */ - { - LEX *lex= Lex; - if (! lex->is_partition_management()) + /* empty */ { - if (lex->part_info->part_type == RANGE_PARTITION) - { - my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), - "RANGE", "LESS THAN"); - MYSQL_YYABORT; - } - if (lex->part_info->part_type == LIST_PARTITION) + LEX *lex= Lex; + if (! lex->is_partition_management()) { - my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), - "LIST", "IN"); - MYSQL_YYABORT; + if (lex->part_info->part_type == RANGE_PARTITION) + { + my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), + "RANGE", "LESS THAN"); + MYSQL_YYABORT; + } + if (lex->part_info->part_type == LIST_PARTITION) + { + my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), + "LIST", "IN"); + MYSQL_YYABORT; + } } + else + lex->part_info->part_type= HASH_PARTITION; } - else - lex->part_info->part_type= HASH_PARTITION; - } | VALUES LESS_SYM THAN_SYM part_func_max - { - LEX *lex= Lex; - if (! lex->is_partition_management()) { - if (Lex->part_info->part_type != RANGE_PARTITION) + LEX *lex= Lex; + if (! lex->is_partition_management()) { - my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), - "RANGE", "LESS THAN"); - MYSQL_YYABORT; + if (Lex->part_info->part_type != RANGE_PARTITION) + { + my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), + "RANGE", "LESS THAN"); + MYSQL_YYABORT; + } } + else + lex->part_info->part_type= RANGE_PARTITION; } - else - lex->part_info->part_type= RANGE_PARTITION; - } | VALUES IN_SYM '(' part_list_func ')' - { - LEX *lex= Lex; - if (! lex->is_partition_management()) { - if (Lex->part_info->part_type != LIST_PARTITION) + LEX *lex= Lex; + if (! lex->is_partition_management()) { - my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), - "LIST", "IN"); - MYSQL_YYABORT; + if (Lex->part_info->part_type != LIST_PARTITION) + { + my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), + "LIST", "IN"); + MYSQL_YYABORT; + } } + else + lex->part_info->part_type= LIST_PARTITION; } - else - lex->part_info->part_type= LIST_PARTITION; - } ; part_func_max: - max_value_sym - { - LEX *lex= Lex; - if (lex->part_info->defined_max_value) + max_value_sym { - my_parse_error(ER(ER_PARTITION_MAXVALUE_ERROR)); - MYSQL_YYABORT; + LEX *lex= Lex; + if (lex->part_info->defined_max_value) + { + 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; + lex->part_info->curr_part_elem->range_value= LONGLONG_MAX; } - lex->part_info->defined_max_value= TRUE; - lex->part_info->curr_part_elem->max_value= TRUE; - lex->part_info->curr_part_elem->range_value= LONGLONG_MAX; - } | part_range_func - { - if (Lex->part_info->defined_max_value) - { - my_parse_error(ER(ER_PARTITION_MAXVALUE_ERROR)); - MYSQL_YYABORT; - } - if (Lex->part_info->curr_part_elem->has_null_value) { - my_parse_error(ER(ER_NULL_IN_VALUES_LESS_THAN)); - MYSQL_YYABORT; + if (Lex->part_info->defined_max_value) + { + my_parse_error(ER(ER_PARTITION_MAXVALUE_ERROR)); + MYSQL_YYABORT; + } + if (Lex->part_info->curr_part_elem->has_null_value) + { + my_parse_error(ER(ER_NULL_IN_VALUES_LESS_THAN)); + MYSQL_YYABORT; + } } - } ; max_value_sym: - MAX_VALUE_SYM + MAX_VALUE_SYM | '(' MAX_VALUE_SYM ')' ; part_range_func: - '(' part_bit_expr ')' - { - partition_info *part_info= Lex->part_info; - if (!($2->unsigned_flag)) - part_info->curr_part_elem->signed_flag= TRUE; - part_info->curr_part_elem->range_value= $2->value; - } + '(' part_bit_expr ')' + { + partition_info *part_info= Lex->part_info; + if (!($2->unsigned_flag)) + part_info->curr_part_elem->signed_flag= TRUE; + part_info->curr_part_elem->range_value= $2->value; + } ; part_list_func: - part_list_item {} + part_list_item {} | part_list_func ',' part_list_item {} ; part_list_item: - part_bit_expr - { - part_elem_value *value_ptr= $1; - partition_info *part_info= Lex->part_info; - if (!value_ptr->unsigned_flag) - part_info->curr_part_elem->signed_flag= TRUE; - if (!value_ptr->null_value && - part_info->curr_part_elem-> - list_val_list.push_back(value_ptr)) - { - mem_alloc_error(sizeof(part_elem_value)); - MYSQL_YYABORT; + part_bit_expr + { + part_elem_value *value_ptr= $1; + partition_info *part_info= Lex->part_info; + if (!value_ptr->unsigned_flag) + part_info->curr_part_elem->signed_flag= TRUE; + if (!value_ptr->null_value && + part_info->curr_part_elem-> + list_val_list.push_back(value_ptr)) + { + mem_alloc_error(sizeof(part_elem_value)); + MYSQL_YYABORT; + } } - } ; part_bit_expr: - bit_expr - { - Item *part_expr= $1; - THD *thd= YYTHD; - 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; - - context->table_list= 0; - thd->where= "partition function"; - - part_elem_value *value_ptr= - (part_elem_value*)sql_alloc(sizeof(part_elem_value)); - if (!value_ptr) - { - mem_alloc_error(sizeof(part_elem_value)); - MYSQL_YYABORT; - } - if (part_expr->walk(&Item::check_partition_func_processor, 0, - NULL)) - { - my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); - MYSQL_YYABORT; - } - if (part_expr->fix_fields(YYTHD, (Item**)0) || - ((context->table_list= save_list), FALSE) || - (!part_expr->const_item()) || - (!lex->safe_to_cache_query)) - { - my_error(ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR, MYF(0)); - MYSQL_YYABORT; - } - thd->where= save_where; - value_ptr->value= part_expr->val_int(); - value_ptr->unsigned_flag= TRUE; - if (!part_expr->unsigned_flag && - value_ptr->value < 0) - value_ptr->unsigned_flag= FALSE; - if ((value_ptr->null_value= part_expr->null_value)) + bit_expr { - if (Lex->part_info->curr_part_elem->has_null_value) + Item *part_expr= $1; + THD *thd= YYTHD; + 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; + + context->table_list= 0; + thd->where= "partition function"; + + part_elem_value *value_ptr= + (part_elem_value*)sql_alloc(sizeof(part_elem_value)); + if (!value_ptr) { - my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0)); + mem_alloc_error(sizeof(part_elem_value)); MYSQL_YYABORT; } - Lex->part_info->curr_part_elem->has_null_value= TRUE; - } - else if (part_expr->result_type() != INT_RESULT && - !part_expr->null_value) - { - my_parse_error(ER(ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR)); - MYSQL_YYABORT; + if (part_expr->walk(&Item::check_partition_func_processor, 0, + NULL)) + { + my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); + MYSQL_YYABORT; + } + if (part_expr->fix_fields(YYTHD, (Item**)0) || + ((context->table_list= save_list), FALSE) || + (!part_expr->const_item()) || + (!lex->safe_to_cache_query)) + { + my_error(ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR, MYF(0)); + MYSQL_YYABORT; + } + thd->where= save_where; + value_ptr->value= part_expr->val_int(); + value_ptr->unsigned_flag= TRUE; + if (!part_expr->unsigned_flag && + value_ptr->value < 0) + value_ptr->unsigned_flag= FALSE; + if ((value_ptr->null_value= part_expr->null_value)) + { + if (Lex->part_info->curr_part_elem->has_null_value) + { + my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0)); + MYSQL_YYABORT; + } + Lex->part_info->curr_part_elem->has_null_value= TRUE; + } + else if (part_expr->result_type() != INT_RESULT) + { + my_parse_error(ER(ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR)); + MYSQL_YYABORT; + } + $$= value_ptr; } - $$= value_ptr; - } ; opt_sub_partition: - /* empty */ - { - if (Lex->part_info->no_subparts != 0 && - !Lex->part_info->use_default_subpartitions) - { - my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); - MYSQL_YYABORT; - } - } - | '(' sub_part_list ')' - { - LEX *lex= Lex; - partition_info *part_info= lex->part_info; - if (part_info->no_subparts != 0) + /* empty */ { - if (part_info->no_subparts != - part_info->count_curr_subparts) + if (Lex->part_info->no_subparts != 0 && + !Lex->part_info->use_default_subpartitions) { my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); MYSQL_YYABORT; } } - else if (part_info->count_curr_subparts > 0) + | '(' sub_part_list ')' { - if (part_info->partitions.elements > 1) + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + if (part_info->no_subparts != 0) { - my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); - MYSQL_YYABORT; + if (part_info->no_subparts != + part_info->count_curr_subparts) + { + my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); + MYSQL_YYABORT; + } } - part_info->no_subparts= part_info->count_curr_subparts; + else if (part_info->count_curr_subparts > 0) + { + if (part_info->partitions.elements > 1) + { + my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); + MYSQL_YYABORT; + } + part_info->no_subparts= part_info->count_curr_subparts; + } + part_info->count_curr_subparts= 0; } - part_info->count_curr_subparts= 0; - } ; sub_part_list: - sub_part_definition {} + sub_part_definition {} | sub_part_list ',' sub_part_definition {} ; sub_part_definition: - SUBPARTITION_SYM - { - LEX *lex= Lex; - partition_info *part_info= lex->part_info; - partition_element *curr_part= part_info->current_partition; - partition_element *sub_p_elem= new partition_element(curr_part); - if (!sub_p_elem || - curr_part->subpartitions.push_back(sub_p_elem)) + SUBPARTITION_SYM { - mem_alloc_error(sizeof(partition_element)); - MYSQL_YYABORT; + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + partition_element *curr_part= part_info->current_partition; + partition_element *sub_p_elem= new partition_element(curr_part); + if (!sub_p_elem || + curr_part->subpartitions.push_back(sub_p_elem)) + { + mem_alloc_error(sizeof(partition_element)); + MYSQL_YYABORT; + } + part_info->curr_part_elem= sub_p_elem; + part_info->use_default_subpartitions= FALSE; + part_info->use_default_no_subpartitions= FALSE; + part_info->count_curr_subparts++; } - part_info->curr_part_elem= sub_p_elem; - part_info->use_default_subpartitions= FALSE; - part_info->use_default_no_subpartitions= FALSE; - part_info->count_curr_subparts++; - } - sub_name opt_part_options {} + sub_name opt_part_options {} ; sub_name: - ident_or_text - { Lex->part_info->curr_part_elem->partition_name= $1.str; } + ident_or_text + { Lex->part_info->curr_part_elem->partition_name= $1.str; } ; opt_part_options: - /* empty */ {} + /* empty */ {} | opt_part_option_list {} ; opt_part_option_list: - opt_part_option_list opt_part_option {} + opt_part_option_list opt_part_option {} | opt_part_option {} ; opt_part_option: - TABLESPACE opt_equal ident_or_text - { Lex->part_info->curr_part_elem->tablespace_name= $3.str; } + TABLESPACE opt_equal ident_or_text + { Lex->part_info->curr_part_elem->tablespace_name= $3.str; } | opt_storage ENGINE_SYM opt_equal storage_engines - { - LEX *lex= Lex; - lex->part_info->curr_part_elem->engine_type= $4; - lex->part_info->default_engine_type= $4; - } + { + LEX *lex= Lex; + lex->part_info->curr_part_elem->engine_type= $4; + lex->part_info->default_engine_type= $4; + } | NODEGROUP_SYM opt_equal real_ulong_num - { Lex->part_info->curr_part_elem->nodegroup_id= (uint16) $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= (ha_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= (ha_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; } + { Lex->part_info->curr_part_elem->data_file_name= $4.str; } | INDEX_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys - { Lex->part_info->curr_part_elem->index_file_name= $4.str; } + { Lex->part_info->curr_part_elem->index_file_name= $4.str; } | COMMENT_SYM opt_equal TEXT_STRING_sys - { Lex->part_info->curr_part_elem->part_comment= $3.str; } + { Lex->part_info->curr_part_elem->part_comment= $3.str; } ; /* @@ -4231,93 +4293,132 @@ opt_part_option: create_select: SELECT_SYM { - LEX *lex=Lex; - lex->lock_option= using_update_log ? TL_READ_NO_INSERT : TL_READ; - if (lex->sql_command == SQLCOM_INSERT) - lex->sql_command= SQLCOM_INSERT_SELECT; - else if (lex->sql_command == SQLCOM_REPLACE) - lex->sql_command= SQLCOM_REPLACE_SELECT; - /* + LEX *lex=Lex; + lex->lock_option= using_update_log ? TL_READ_NO_INSERT : TL_READ; + if (lex->sql_command == SQLCOM_INSERT) + lex->sql_command= SQLCOM_INSERT_SELECT; + else if (lex->sql_command == SQLCOM_REPLACE) + lex->sql_command= SQLCOM_REPLACE_SELECT; + /* The following work only with the local list, the global list is created correctly in this case - */ - lex->current_select->table_list.save_and_clear(&lex->save_list); - mysql_init_select(lex); - lex->current_select->parsing_place= SELECT_LIST; + */ + lex->current_select->table_list.save_and_clear(&lex->save_list); + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; } select_options select_item_list - { - Select->parsing_place= NO_MATTER; - } - opt_select_from - { - /* + { + Select->parsing_place= NO_MATTER; + } + opt_select_from + { + /* The following work only with the local list, the global list is created correctly in this case - */ - Lex->current_select->table_list.push_front(&Lex->save_list); - } + */ + Lex->current_select->table_list.push_front(&Lex->save_list); + } ; opt_as: - /* empty */ {} - | AS {}; + /* empty */ {} + | AS {} + ; opt_create_database_options: - /* empty */ {} - | create_database_options {}; + /* empty */ {} + | create_database_options {} + ; create_database_options: - create_database_option {} - | create_database_options create_database_option {}; + create_database_option {} + | create_database_options create_database_option {} + ; create_database_option: - default_collation {} - | default_charset {}; + default_collation {} + | default_charset {} + ; opt_table_options: - /* empty */ { $$= 0; } - | table_options { $$= $1;}; + /* empty */ { $$= 0; } + | table_options { $$= $1;} + ; table_options: - table_option { $$=$1; } - | table_option table_options { $$= $1 | $2; }; + table_option { $$=$1; } + | table_option table_options { $$= $1 | $2; } + ; table_option: - TEMPORARY { $$=HA_LEX_CREATE_TMP_TABLE; }; + TEMPORARY { $$=HA_LEX_CREATE_TMP_TABLE; } + ; opt_if_not_exists: - /* empty */ { $$= 0; } - | IF not EXISTS { $$=HA_LEX_CREATE_IF_NOT_EXISTS; }; + /* empty */ { $$= 0; } + | IF not EXISTS { $$=HA_LEX_CREATE_IF_NOT_EXISTS; } + ; opt_create_table_options: - /* empty */ - | create_table_options; + /* empty */ + | create_table_options + ; create_table_options_space_separated: - create_table_option - | create_table_option create_table_options_space_separated; + create_table_option + | create_table_option create_table_options_space_separated + ; create_table_options: - create_table_option - | create_table_option create_table_options - | create_table_option ',' create_table_options; + create_table_option + | create_table_option create_table_options + | create_table_option ',' create_table_options + ; create_table_option: - ENGINE_SYM opt_equal storage_engines { Lex->create_info.db_type= $3; Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; } - | TYPE_SYM opt_equal storage_engines + ENGINE_SYM opt_equal storage_engines + { + Lex->create_info.db_type= $3; + Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; + } + | TYPE_SYM opt_equal storage_engines { Lex->create_info.db_type= $3; WARN_DEPRECATED(yythd, "5.2", "TYPE=storage_engine", "'ENGINE=storage_engine'"); Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; } - | MAX_ROWS opt_equal ulonglong_num { Lex->create_info.max_rows= $3; Lex->create_info.used_fields|= HA_CREATE_USED_MAX_ROWS;} - | MIN_ROWS opt_equal ulonglong_num { Lex->create_info.min_rows= $3; Lex->create_info.used_fields|= HA_CREATE_USED_MIN_ROWS;} - | AVG_ROW_LENGTH opt_equal ulong_num { Lex->create_info.avg_row_length=$3; Lex->create_info.used_fields|= HA_CREATE_USED_AVG_ROW_LENGTH;} - | PASSWORD opt_equal TEXT_STRING_sys { Lex->create_info.password=$3.str; Lex->create_info.used_fields|= HA_CREATE_USED_PASSWORD; } - | COMMENT_SYM opt_equal TEXT_STRING_sys { Lex->create_info.comment=$3; Lex->create_info.used_fields|= HA_CREATE_USED_COMMENT; } - | AUTO_INC opt_equal ulonglong_num { Lex->create_info.auto_increment_value=$3; Lex->create_info.used_fields|= HA_CREATE_USED_AUTO;} + | MAX_ROWS opt_equal ulonglong_num + { + Lex->create_info.max_rows= $3; + Lex->create_info.used_fields|= HA_CREATE_USED_MAX_ROWS; + } + | MIN_ROWS opt_equal ulonglong_num + { + Lex->create_info.min_rows= $3; + Lex->create_info.used_fields|= HA_CREATE_USED_MIN_ROWS; + } + | AVG_ROW_LENGTH opt_equal ulong_num + { + Lex->create_info.avg_row_length=$3; + Lex->create_info.used_fields|= HA_CREATE_USED_AVG_ROW_LENGTH; + } + | PASSWORD opt_equal TEXT_STRING_sys + { + Lex->create_info.password=$3.str; + Lex->create_info.used_fields|= HA_CREATE_USED_PASSWORD; + } + | COMMENT_SYM opt_equal TEXT_STRING_sys + { + Lex->create_info.comment=$3; + Lex->create_info.used_fields|= HA_CREATE_USED_COMMENT; + } + | AUTO_INC opt_equal ulonglong_num + { + Lex->create_info.auto_increment_value=$3; + Lex->create_info.used_fields|= HA_CREATE_USED_AUTO; + } | PACK_KEYS_SYM opt_equal ulong_num { switch($3) { @@ -4339,44 +4440,80 @@ create_table_option: ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS); Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS; } - | CHECKSUM_SYM opt_equal ulong_num { Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM; } - | TABLE_CHECKSUM_SYM opt_equal ulong_num { Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM; } - | PAGE_CHECKSUM_SYM opt_equal choice - { - Lex->create_info.used_fields|= HA_CREATE_USED_PAGE_CHECKSUM; + | CHECKSUM_SYM opt_equal ulong_num + { + Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; + Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM; + } + | TABLE_CHECKSUM_SYM opt_equal ulong_num + { + Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; + Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM; + } + | PAGE_CHECKSUM_SYM opt_equal choice + { + Lex->create_info.used_fields|= HA_CREATE_USED_PAGE_CHECKSUM; Lex->create_info.page_checksum= $3; } - | DELAY_KEY_WRITE_SYM opt_equal ulong_num { Lex->create_info.table_options|= $3 ? HA_OPTION_DELAY_KEY_WRITE : HA_OPTION_NO_DELAY_KEY_WRITE; Lex->create_info.used_fields|= HA_CREATE_USED_DELAY_KEY_WRITE; } - | ROW_FORMAT_SYM opt_equal row_types { Lex->create_info.row_type= $3; Lex->create_info.used_fields|= HA_CREATE_USED_ROW_FORMAT; } - | UNION_SYM opt_equal '(' table_list ')' - { - /* Move the union list to the merge_list */ - LEX *lex=Lex; - TABLE_LIST *table_list= lex->select_lex.get_table_list(); - lex->create_info.merge_list= lex->select_lex.table_list; - lex->create_info.merge_list.elements--; - lex->create_info.merge_list.first= - (uchar*) (table_list->next_local); - lex->select_lex.table_list.elements=1; - lex->select_lex.table_list.next= - (uchar**) &(table_list->next_local); - table_list->next_local= 0; - lex->create_info.used_fields|= HA_CREATE_USED_UNION; - } - | default_charset - | default_collation - | INSERT_METHOD opt_equal merge_insert_types { Lex->create_info.merge_insert_method= $3; Lex->create_info.used_fields|= HA_CREATE_USED_INSERT_METHOD;} - | 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.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 - { - Lex->create_info.used_fields|= HA_CREATE_USED_KEY_BLOCK_SIZE; - Lex->create_info.key_block_size= $3; - } + | DELAY_KEY_WRITE_SYM opt_equal ulong_num + { + Lex->create_info.table_options|= $3 ? HA_OPTION_DELAY_KEY_WRITE : HA_OPTION_NO_DELAY_KEY_WRITE; + Lex->create_info.used_fields|= HA_CREATE_USED_DELAY_KEY_WRITE; + } + | ROW_FORMAT_SYM opt_equal row_types + { + Lex->create_info.row_type= $3; + Lex->create_info.used_fields|= HA_CREATE_USED_ROW_FORMAT; + } + | UNION_SYM opt_equal '(' table_list ')' + { + /* Move the union list to the merge_list */ + LEX *lex=Lex; + TABLE_LIST *table_list= lex->select_lex.get_table_list(); + lex->create_info.merge_list= lex->select_lex.table_list; + lex->create_info.merge_list.elements--; + lex->create_info.merge_list.first= + (uchar*) (table_list->next_local); + lex->select_lex.table_list.elements=1; + lex->select_lex.table_list.next= + (uchar**) &(table_list->next_local); + table_list->next_local= 0; + lex->create_info.used_fields|= HA_CREATE_USED_UNION; + } + | default_charset + | default_collation + | INSERT_METHOD opt_equal merge_insert_types + { + Lex->create_info.merge_insert_method= $3; + Lex->create_info.used_fields|= HA_CREATE_USED_INSERT_METHOD; + } + | 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.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 + { + Lex->create_info.used_fields|= HA_CREATE_USED_KEY_BLOCK_SIZE; + Lex->create_info.key_block_size= $3; + } | TRANSACTIONAL_SYM opt_equal choice { Lex->create_info.used_fields|= HA_CREATE_USED_TRANSACTIONAL; @@ -4385,148 +4522,154 @@ create_table_option: ; default_charset: - opt_default charset opt_equal charset_name_or_default - { - HA_CREATE_INFO *cinfo= &Lex->create_info; - if ((cinfo->used_fields & HA_CREATE_USED_DEFAULT_CHARSET) && - cinfo->default_table_charset && $4 && - !my_charset_same(cinfo->default_table_charset,$4)) - { - my_error(ER_CONFLICTING_DECLARATIONS, MYF(0), - "CHARACTER SET ", cinfo->default_table_charset->csname, - "CHARACTER SET ", $4->csname); - MYSQL_YYABORT; - } - Lex->create_info.default_table_charset= $4; - Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; - }; - -default_collation: - opt_default COLLATE_SYM opt_equal collation_name_or_default - { - HA_CREATE_INFO *cinfo= &Lex->create_info; - if ((cinfo->used_fields & HA_CREATE_USED_DEFAULT_CHARSET) && - cinfo->default_table_charset && $4 && - !my_charset_same(cinfo->default_table_charset,$4)) + opt_default charset opt_equal charset_name_or_default + { + HA_CREATE_INFO *cinfo= &Lex->create_info; + if ((cinfo->used_fields & HA_CREATE_USED_DEFAULT_CHARSET) && + cinfo->default_table_charset && $4 && + !my_charset_same(cinfo->default_table_charset,$4)) { - my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), - $4->name, cinfo->default_table_charset->csname); + my_error(ER_CONFLICTING_DECLARATIONS, MYF(0), + "CHARACTER SET ", cinfo->default_table_charset->csname, + "CHARACTER SET ", $4->csname); MYSQL_YYABORT; } Lex->create_info.default_table_charset= $4; Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; - }; + } + ; -storage_engines: - ident_or_text - { - plugin_ref plugin= ha_resolve_by_name(YYTHD, &$1); +default_collation: + opt_default COLLATE_SYM opt_equal collation_name_or_default + { + HA_CREATE_INFO *cinfo= &Lex->create_info; + if ((cinfo->used_fields & HA_CREATE_USED_DEFAULT_CHARSET) && + cinfo->default_table_charset && $4 && + !my_charset_same(cinfo->default_table_charset,$4)) + { + my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), + $4->name, cinfo->default_table_charset->csname); + MYSQL_YYABORT; + } + Lex->create_info.default_table_charset= $4; + Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; + } + ; - if (plugin) - $$= plugin_data(plugin, handlerton*); - else +storage_engines: + ident_or_text { - if (YYTHD->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION) + plugin_ref plugin= ha_resolve_by_name(YYTHD, &$1); + + if (plugin) + $$= plugin_data(plugin, handlerton*); + else { - my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str); - MYSQL_YYABORT; + if (YYTHD->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION) + { + my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str); + MYSQL_YYABORT; + } + $$= 0; + push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_UNKNOWN_STORAGE_ENGINE, + ER(ER_UNKNOWN_STORAGE_ENGINE), + $1.str); } - $$= 0; - push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_UNKNOWN_STORAGE_ENGINE, - ER(ER_UNKNOWN_STORAGE_ENGINE), - $1.str); } - } ; known_storage_engines: - ident_or_text - { - plugin_ref plugin; - if ((plugin= ha_resolve_by_name(YYTHD, &$1))) - $$= plugin_data(plugin, handlerton*); - else - { - my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str); - MYSQL_YYABORT; - } - } + ident_or_text + { + plugin_ref plugin; + if ((plugin= ha_resolve_by_name(YYTHD, &$1))) + $$= plugin_data(plugin, handlerton*); + else + { + my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str); + MYSQL_YYABORT; + } + } ; row_types: - DEFAULT { $$= ROW_TYPE_DEFAULT; } - | FIXED_SYM { $$= ROW_TYPE_FIXED; } - | DYNAMIC_SYM { $$= ROW_TYPE_DYNAMIC; } - | COMPRESSED_SYM { $$= ROW_TYPE_COMPRESSED; } - | REDUNDANT_SYM { $$= ROW_TYPE_REDUNDANT; } - | COMPACT_SYM { $$= ROW_TYPE_COMPACT; } - | PAGE_SYM { $$= ROW_TYPE_PAGE; }; + DEFAULT { $$= ROW_TYPE_DEFAULT; } + | FIXED_SYM { $$= ROW_TYPE_FIXED; } + | DYNAMIC_SYM { $$= ROW_TYPE_DYNAMIC; } + | COMPRESSED_SYM { $$= ROW_TYPE_COMPRESSED; } + | REDUNDANT_SYM { $$= ROW_TYPE_REDUNDANT; } + | COMPACT_SYM { $$= ROW_TYPE_COMPACT; } + | PAGE_SYM { $$= ROW_TYPE_PAGE; } + ; merge_insert_types: - NO_SYM { $$= MERGE_INSERT_DISABLED; } + NO_SYM { $$= MERGE_INSERT_DISABLED; } | FIRST_SYM { $$= MERGE_INSERT_TO_FIRST; } - | LAST_SYM { $$= MERGE_INSERT_TO_LAST; }; + | LAST_SYM { $$= MERGE_INSERT_TO_LAST; } + ; opt_select_from: - opt_limit_clause {} - | select_from select_lock_type; + opt_limit_clause {} + | select_from select_lock_type + ; udf_func_type: - /* empty */ { $$ = UDFTYPE_FUNCTION; } - | AGGREGATE_SYM { $$ = UDFTYPE_AGGREGATE; }; + /* empty */ { $$ = UDFTYPE_FUNCTION; } + | AGGREGATE_SYM { $$ = UDFTYPE_AGGREGATE; }; udf_type: - STRING_SYM {$$ = (int) STRING_RESULT; } - | REAL {$$ = (int) REAL_RESULT; } + STRING_SYM {$$ = (int) STRING_RESULT; } + | REAL {$$ = (int) REAL_RESULT; } | DECIMAL_SYM {$$ = (int) DECIMAL_RESULT; } - | INT_SYM {$$ = (int) INT_RESULT; }; + | INT_SYM {$$ = (int) INT_RESULT; } + ; field_list: - field_list_item - | field_list ',' field_list_item; - + field_list_item + | field_list ',' field_list_item + ; field_list_item: - column_def - | key_def - ; + column_def + | key_def + ; column_def: - field_spec opt_check_constraint - | field_spec references - { - Lex->col_list.empty(); /* Alloced by sql_alloc */ - } - ; + field_spec opt_check_constraint + | field_spec references + { + Lex->col_list.empty(); /* Alloced by sql_alloc */ + } + ; key_def: - key_type opt_ident key_alg '(' key_list ')' key_options - { - LEX *lex=Lex; - if ($1 != Key::FULLTEXT && lex->key_create_info.parser_name.str) - { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; - } + key_type opt_ident key_alg '(' key_list ')' key_options + { + LEX *lex=Lex; + if ($1 != Key::FULLTEXT && lex->key_create_info.parser_name.str) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } Key *key= new Key($1, $2, &lex->key_create_info, 0, lex->col_list); - lex->alter_info.key_list.push_back(key); - lex->col_list.empty(); /* Alloced by sql_alloc */ - } - | opt_constraint constraint_key_type opt_ident key_alg - '(' key_list ')' key_options - { - LEX *lex=Lex; - const char *key_name= $3 ? $3 : $1; + lex->alter_info.key_list.push_back(key); + lex->col_list.empty(); /* Alloced by sql_alloc */ + } + | opt_constraint constraint_key_type opt_ident key_alg + '(' key_list ')' key_options + { + LEX *lex=Lex; + const char *key_name= $3 ? $3 : $1; Key *key= new Key($2, key_name, &lex->key_create_info, 0, lex->col_list); - lex->alter_info.key_list.push_back(key); - lex->col_list.empty(); /* Alloced by sql_alloc */ - } - | opt_constraint FOREIGN KEY_SYM opt_ident '(' key_list ')' references - { - LEX *lex=Lex; + lex->alter_info.key_list.push_back(key); + lex->col_list.empty(); /* Alloced by sql_alloc */ + } + | opt_constraint FOREIGN KEY_SYM opt_ident '(' key_list ')' references + { + LEX *lex=Lex; const char *key_name= $4 ? $4 : $1; Key *key= new Foreign_key(key_name, lex->col_list, $8, @@ -4539,117 +4682,168 @@ key_def: &default_key_create_info, 1, lex->col_list); lex->alter_info.key_list.push_back(key); - lex->col_list.empty(); /* Alloced by sql_alloc */ + lex->col_list.empty(); /* Alloced by sql_alloc */ /* Only used for ALTER TABLE. Ignored otherwise. */ lex->alter_info.flags|= ALTER_FOREIGN_KEY; - } - | constraint opt_check_constraint - { - Lex->col_list.empty(); /* Alloced by sql_alloc */ - } - | opt_constraint check_constraint - { - Lex->col_list.empty(); /* Alloced by sql_alloc */ - } - ; + } + | constraint opt_check_constraint + { + Lex->col_list.empty(); /* Alloced by sql_alloc */ + } + | opt_constraint check_constraint + { + Lex->col_list.empty(); /* Alloced by sql_alloc */ + } + ; opt_check_constraint: - /* empty */ - | check_constraint - ; + /* empty */ + | check_constraint + ; check_constraint: - CHECK_SYM expr - ; + CHECK_SYM expr + ; opt_constraint: - /* empty */ { $$=(char*) 0; } - | constraint { $$= $1; } - ; + /* empty */ { $$=(char*) 0; } + | constraint { $$= $1; } + ; constraint: - CONSTRAINT opt_ident { $$=$2; } - ; + CONSTRAINT opt_ident { $$=$2; } + ; field_spec: - field_ident - { - LEX *lex=Lex; - lex->length=lex->dec=0; lex->type=0; - lex->default_value= lex->on_update_value= 0; - lex->comment=null_lex_str; - lex->charset=NULL; - } - type opt_attribute - { - LEX *lex=Lex; - if (add_field_to_list(lex->thd, &$1, (enum enum_field_types) $3, - lex->length,lex->dec,lex->type, - lex->default_value, lex->on_update_value, - &lex->comment, - lex->change,&lex->interval_list,lex->charset, - lex->uint_geom_type)) - MYSQL_YYABORT; - }; + field_ident + { + LEX *lex=Lex; + lex->length=lex->dec=0; + lex->type=0; + lex->default_value= lex->on_update_value= 0; + lex->comment=null_lex_str; + lex->charset=NULL; + } + type opt_attribute + { + LEX *lex=Lex; + if (add_field_to_list(lex->thd, &$1, (enum enum_field_types) $3, + lex->length,lex->dec,lex->type, + lex->default_value, lex->on_update_value, + &lex->comment, + lex->change,&lex->interval_list,lex->charset, + lex->uint_geom_type)) + MYSQL_YYABORT; + } + ; type: - int_type opt_len field_options { $$=$1; } - | real_type opt_precision field_options { $$=$1; } - | FLOAT_SYM float_options field_options { $$=MYSQL_TYPE_FLOAT; } - | BIT_SYM { Lex->length= (char*) "1"; - $$=MYSQL_TYPE_BIT; } - | BIT_SYM '(' NUM ')' { Lex->length= $3.str; - $$=MYSQL_TYPE_BIT; } - | BOOL_SYM { Lex->length=(char*) "1"; - $$=MYSQL_TYPE_TINY; } - | BOOLEAN_SYM { Lex->length=(char*) "1"; - $$=MYSQL_TYPE_TINY; } - | char '(' NUM ')' opt_binary { Lex->length=$3.str; - $$=MYSQL_TYPE_STRING; } - | char opt_binary { Lex->length=(char*) "1"; - $$=MYSQL_TYPE_STRING; } - | nchar '(' NUM ')' opt_bin_mod { Lex->length=$3.str; - $$=MYSQL_TYPE_STRING; - Lex->charset=national_charset_info; } - | nchar opt_bin_mod { Lex->length=(char*) "1"; - $$=MYSQL_TYPE_STRING; - Lex->charset=national_charset_info; } - | BINARY '(' NUM ')' { Lex->length=$3.str; - Lex->charset=&my_charset_bin; - $$=MYSQL_TYPE_STRING; } - | BINARY { Lex->length= (char*) "1"; - Lex->charset=&my_charset_bin; - $$=MYSQL_TYPE_STRING; } - | varchar '(' NUM ')' opt_binary { Lex->length=$3.str; - $$= MYSQL_TYPE_VARCHAR; } - | nvarchar '(' NUM ')' opt_bin_mod { Lex->length=$3.str; - $$= MYSQL_TYPE_VARCHAR; - Lex->charset=national_charset_info; } - | VARBINARY '(' NUM ')' { Lex->length=$3.str; - Lex->charset=&my_charset_bin; - $$= MYSQL_TYPE_VARCHAR; } - | 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) - $$=MYSQL_TYPE_DATETIME; - else + int_type opt_len field_options { $$=$1; } + | real_type opt_precision field_options { $$=$1; } + | FLOAT_SYM float_options field_options { $$=MYSQL_TYPE_FLOAT; } + | BIT_SYM + { + Lex->length= (char*) "1"; + $$=MYSQL_TYPE_BIT; + } + | BIT_SYM '(' NUM ')' + { + Lex->length= $3.str; + $$=MYSQL_TYPE_BIT; + } + | BOOL_SYM + { + Lex->length=(char*) "1"; + $$=MYSQL_TYPE_TINY; + } + | BOOLEAN_SYM + { + Lex->length=(char*) "1"; + $$=MYSQL_TYPE_TINY; + } + | char '(' NUM ')' opt_binary + { + Lex->length=$3.str; + $$=MYSQL_TYPE_STRING; + } + | char opt_binary + { + Lex->length=(char*) "1"; + $$=MYSQL_TYPE_STRING; + } + | nchar '(' NUM ')' opt_bin_mod + { + Lex->length=$3.str; + $$=MYSQL_TYPE_STRING; + Lex->charset=national_charset_info; + } + | nchar opt_bin_mod + { + Lex->length=(char*) "1"; + $$=MYSQL_TYPE_STRING; + Lex->charset=national_charset_info; + } + | BINARY '(' NUM ')' + { + Lex->length=$3.str; + Lex->charset=&my_charset_bin; + $$=MYSQL_TYPE_STRING; + } + | BINARY + { + Lex->length= (char*) "1"; + Lex->charset=&my_charset_bin; + $$=MYSQL_TYPE_STRING; + } + | varchar '(' NUM ')' opt_binary + { + Lex->length=$3.str; + $$= MYSQL_TYPE_VARCHAR; + } + | nvarchar '(' NUM ')' opt_bin_mod + { + Lex->length=$3.str; + $$= MYSQL_TYPE_VARCHAR; + Lex->charset=national_charset_info; + } + | VARBINARY '(' NUM ')' + { + Lex->length=$3.str; + Lex->charset=&my_charset_bin; + $$= MYSQL_TYPE_VARCHAR; + } + | 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) + $$=MYSQL_TYPE_DATETIME; + else { /* Unlike other types TIMESTAMP fields are NOT NULL by default. */ Lex->type|= NOT_NULL_FLAG; - $$=MYSQL_TYPE_TIMESTAMP; + $$=MYSQL_TYPE_TIMESTAMP; } - } - | DATETIME { $$=MYSQL_TYPE_DATETIME; } - | TINYBLOB { Lex->charset=&my_charset_bin; - $$=MYSQL_TYPE_TINY_BLOB; } - | BLOB_SYM opt_len { Lex->charset=&my_charset_bin; - $$=MYSQL_TYPE_BLOB; } - | spatial_type + } + | DATETIME + { $$=MYSQL_TYPE_DATETIME; } + | TINYBLOB + { + Lex->charset=&my_charset_bin; + $$=MYSQL_TYPE_TINY_BLOB; + } + | BLOB_SYM opt_len + { + Lex->charset=&my_charset_bin; + $$=MYSQL_TYPE_BLOB; + } + | spatial_type { #ifdef HAVE_SPATIAL Lex->charset=&my_charset_bin; @@ -4661,379 +4855,439 @@ type: MYSQL_YYABORT; #endif } - | MEDIUMBLOB { Lex->charset=&my_charset_bin; - $$=MYSQL_TYPE_MEDIUM_BLOB; } - | LONGBLOB { Lex->charset=&my_charset_bin; - $$=MYSQL_TYPE_LONG_BLOB; } - | LONG_SYM VARBINARY { Lex->charset=&my_charset_bin; - $$=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 - { $$=MYSQL_TYPE_NEWDECIMAL;} - | NUMERIC_SYM float_options field_options - { $$=MYSQL_TYPE_NEWDECIMAL;} - | FIXED_SYM float_options field_options - { $$=MYSQL_TYPE_NEWDECIMAL;} - | ENUM {Lex->interval_list.empty();} '(' string_list ')' opt_binary - { $$=MYSQL_TYPE_ENUM; } - | SET { Lex->interval_list.empty();} '(' string_list ')' opt_binary - { $$=MYSQL_TYPE_SET; } - | LONG_SYM opt_binary { $$=MYSQL_TYPE_MEDIUM_BLOB; } - | SERIAL_SYM - { - $$=MYSQL_TYPE_LONGLONG; - Lex->type|= (AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNSIGNED_FLAG | - UNIQUE_FLAG); - } - ; + | MEDIUMBLOB + { + Lex->charset=&my_charset_bin; + $$=MYSQL_TYPE_MEDIUM_BLOB; + } + | LONGBLOB + { + Lex->charset=&my_charset_bin; + $$=MYSQL_TYPE_LONG_BLOB; + } + | LONG_SYM VARBINARY + { + Lex->charset=&my_charset_bin; + $$=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 + { $$=MYSQL_TYPE_NEWDECIMAL;} + | NUMERIC_SYM float_options field_options + { $$=MYSQL_TYPE_NEWDECIMAL;} + | FIXED_SYM float_options field_options + { $$=MYSQL_TYPE_NEWDECIMAL;} + | ENUM + {Lex->interval_list.empty();} + '(' string_list ')' opt_binary + { $$=MYSQL_TYPE_ENUM; } + | SET + { Lex->interval_list.empty();} + '(' string_list ')' opt_binary + { $$=MYSQL_TYPE_SET; } + | LONG_SYM opt_binary + { $$=MYSQL_TYPE_MEDIUM_BLOB; } + | SERIAL_SYM + { + $$=MYSQL_TYPE_LONGLONG; + Lex->type|= (AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNSIGNED_FLAG | + UNIQUE_FLAG); + } + ; spatial_type: - GEOMETRY_SYM { $$= Field::GEOM_GEOMETRY; } - | GEOMETRYCOLLECTION { $$= Field::GEOM_GEOMETRYCOLLECTION; } - | POINT_SYM { Lex->length= (char*)"21"; - $$= Field::GEOM_POINT; - } - | MULTIPOINT { $$= Field::GEOM_MULTIPOINT; } - | LINESTRING { $$= Field::GEOM_LINESTRING; } - | MULTILINESTRING { $$= Field::GEOM_MULTILINESTRING; } - | POLYGON { $$= Field::GEOM_POLYGON; } - | MULTIPOLYGON { $$= Field::GEOM_MULTIPOLYGON; } - ; + GEOMETRY_SYM { $$= Field::GEOM_GEOMETRY; } + | GEOMETRYCOLLECTION { $$= Field::GEOM_GEOMETRYCOLLECTION; } + | POINT_SYM + { + Lex->length= (char*)"21"; + $$= Field::GEOM_POINT; + } + | MULTIPOINT { $$= Field::GEOM_MULTIPOINT; } + | LINESTRING { $$= Field::GEOM_LINESTRING; } + | MULTILINESTRING { $$= Field::GEOM_MULTILINESTRING; } + | POLYGON { $$= Field::GEOM_POLYGON; } + | MULTIPOLYGON { $$= Field::GEOM_MULTIPOLYGON; } + ; char: - CHAR_SYM {} - ; + CHAR_SYM {} + ; nchar: - NCHAR_SYM {} - | NATIONAL_SYM CHAR_SYM {} - ; + NCHAR_SYM {} + | NATIONAL_SYM CHAR_SYM {} + ; varchar: - char VARYING {} - | VARCHAR {} - ; + char VARYING {} + | VARCHAR {} + ; nvarchar: - NATIONAL_SYM VARCHAR {} - | NVARCHAR_SYM {} - | NCHAR_SYM VARCHAR {} - | NATIONAL_SYM CHAR_SYM VARYING {} - | NCHAR_SYM VARYING {} - ; + NATIONAL_SYM VARCHAR {} + | NVARCHAR_SYM {} + | NCHAR_SYM VARCHAR {} + | NATIONAL_SYM CHAR_SYM VARYING {} + | NCHAR_SYM VARYING {} + ; int_type: - INT_SYM { $$=MYSQL_TYPE_LONG; } - | TINYINT { $$=MYSQL_TYPE_TINY; } - | SMALLINT { $$=MYSQL_TYPE_SHORT; } - | MEDIUMINT { $$=MYSQL_TYPE_INT24; } - | BIGINT { $$=MYSQL_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 ? - MYSQL_TYPE_FLOAT : MYSQL_TYPE_DOUBLE; } - | DOUBLE_SYM { $$=MYSQL_TYPE_DOUBLE; } - | DOUBLE_SYM PRECISION { $$=MYSQL_TYPE_DOUBLE; }; - + REAL + { + $$= YYTHD->variables.sql_mode & MODE_REAL_AS_FLOAT ? + MYSQL_TYPE_FLOAT : MYSQL_TYPE_DOUBLE; + } + | DOUBLE_SYM + { $$=MYSQL_TYPE_DOUBLE; } + | DOUBLE_SYM PRECISION + { $$=MYSQL_TYPE_DOUBLE; } + ; float_options: - /* empty */ { Lex->dec=Lex->length= (char*)0; } - | '(' NUM ')' { Lex->length=$2.str; Lex->dec= (char*)0; } - | precision {}; + /* empty */ + { Lex->dec=Lex->length= (char*)0; } + | '(' NUM ')' + { Lex->length=$2.str; Lex->dec= (char*)0; } + | precision + {} + ; precision: - '(' NUM ',' NUM ')' - { - LEX *lex=Lex; - lex->length=$2.str; lex->dec=$4.str; - }; + '(' NUM ',' NUM ')' + { + LEX *lex=Lex; + lex->length=$2.str; + lex->dec=$4.str; + } + ; field_options: - /* empty */ {} - | field_opt_list {}; + /* empty */ {} + | field_opt_list {} + ; field_opt_list: - field_opt_list field_option {} - | field_option {}; + field_opt_list field_option {} + | field_option {} + ; field_option: - SIGNED_SYM {} - | UNSIGNED { Lex->type|= UNSIGNED_FLAG;} - | ZEROFILL { Lex->type|= UNSIGNED_FLAG | ZEROFILL_FLAG; }; + SIGNED_SYM {} + | UNSIGNED { Lex->type|= UNSIGNED_FLAG;} + | ZEROFILL { Lex->type|= UNSIGNED_FLAG | ZEROFILL_FLAG; } + ; opt_len: - /* empty */ { Lex->length=(char*) 0; } /* use default length */ - | '(' NUM ')' { Lex->length= $2.str; }; + /* empty */ { Lex->length=(char*) 0; /* use default length */ } + | '(' NUM ')' { Lex->length= $2.str; } + ; opt_precision: - /* empty */ {} - | precision {}; + /* empty */ {} + | precision {} + ; opt_attribute: - /* empty */ {} - | opt_attribute_list {}; + /* empty */ {} + | opt_attribute_list {} + ; opt_attribute_list: - opt_attribute_list attribute {} - | attribute; + opt_attribute_list attribute {} + | attribute + ; attribute: - NULL_SYM { Lex->type&= ~ NOT_NULL_FLAG; } - | not NULL_SYM { Lex->type|= NOT_NULL_FLAG; } - | DEFAULT now_or_signed_literal { Lex->default_value=$2; } - | ON UPDATE_SYM NOW_SYM optional_braces + NULL_SYM { Lex->type&= ~ NOT_NULL_FLAG; } + | not NULL_SYM { Lex->type|= NOT_NULL_FLAG; } + | DEFAULT now_or_signed_literal { Lex->default_value=$2; } + | ON UPDATE_SYM NOW_SYM optional_braces { Lex->on_update_value= new Item_func_now_local(); } - | AUTO_INC { Lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG; } - | SERIAL_SYM DEFAULT VALUE_SYM - { - LEX *lex=Lex; - lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNIQUE_FLAG; - lex->alter_info.flags|= ALTER_ADD_INDEX; - } - | opt_primary KEY_SYM - { - LEX *lex=Lex; - lex->type|= PRI_KEY_FLAG | NOT_NULL_FLAG; - lex->alter_info.flags|= ALTER_ADD_INDEX; - } - | UNIQUE_SYM - { - LEX *lex=Lex; - lex->type|= UNIQUE_FLAG; - lex->alter_info.flags|= ALTER_ADD_INDEX; - } - | UNIQUE_SYM KEY_SYM - { - LEX *lex=Lex; - lex->type|= UNIQUE_KEY_FLAG; - lex->alter_info.flags|= ALTER_ADD_INDEX; - } - | COMMENT_SYM TEXT_STRING_sys { Lex->comment= $2; } - | COLLATE_SYM collation_name - { - if (Lex->charset && !my_charset_same(Lex->charset,$2)) - { - my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), + | AUTO_INC { Lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG; } + | SERIAL_SYM DEFAULT VALUE_SYM + { + LEX *lex=Lex; + lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNIQUE_FLAG; + lex->alter_info.flags|= ALTER_ADD_INDEX; + } + | opt_primary KEY_SYM + { + LEX *lex=Lex; + lex->type|= PRI_KEY_FLAG | NOT_NULL_FLAG; + lex->alter_info.flags|= ALTER_ADD_INDEX; + } + | UNIQUE_SYM + { + LEX *lex=Lex; + lex->type|= UNIQUE_FLAG; + lex->alter_info.flags|= ALTER_ADD_INDEX; + } + | UNIQUE_SYM KEY_SYM + { + LEX *lex=Lex; + lex->type|= UNIQUE_KEY_FLAG; + lex->alter_info.flags|= ALTER_ADD_INDEX; + } + | COMMENT_SYM TEXT_STRING_sys { Lex->comment= $2; } + | COLLATE_SYM collation_name + { + if (Lex->charset && !my_charset_same(Lex->charset,$2)) + { + my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), $2->name,Lex->charset->csname); - MYSQL_YYABORT; - } - else - { - Lex->charset=$2; - } - } - ; + MYSQL_YYABORT; + } + else + { + Lex->charset=$2; + } + } + ; now_or_signed_literal: - NOW_SYM optional_braces { $$= new Item_func_now_local(); } - | signed_literal { $$=$1; } + NOW_SYM optional_braces + { $$= new Item_func_now_local(); } + | signed_literal + { $$=$1; } ; charset: - CHAR_SYM SET {} - | CHARSET {} - ; + CHAR_SYM SET {} + | CHARSET {} + ; charset_name: - ident_or_text - { - if (!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0)))) - { - my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str); - MYSQL_YYABORT; - } - } - | BINARY { $$= &my_charset_bin; } - ; + ident_or_text + { + if (!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0)))) + { + my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str); + MYSQL_YYABORT; + } + } + | BINARY { $$= &my_charset_bin; } + ; charset_name_or_default: - charset_name { $$=$1; } - | DEFAULT { $$=NULL; } ; + charset_name { $$=$1; } + | DEFAULT { $$=NULL; } + ; opt_load_data_charset: - /* Empty */ { $$= NULL; } - | charset charset_name_or_default { $$= $2; } - ; + /* Empty */ { $$= NULL; } + | charset charset_name_or_default { $$= $2; } + ; old_or_new_charset_name: - ident_or_text - { - if (!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0))) && - !($$=get_old_charset_by_name($1.str))) - { - my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str); - MYSQL_YYABORT; - } - } - | BINARY { $$= &my_charset_bin; } - ; + ident_or_text + { + if (!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0))) && + !($$=get_old_charset_by_name($1.str))) + { + my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str); + MYSQL_YYABORT; + } + } + | BINARY { $$= &my_charset_bin; } + ; old_or_new_charset_name_or_default: - old_or_new_charset_name { $$=$1; } - | DEFAULT { $$=NULL; } ; + old_or_new_charset_name { $$=$1; } + | DEFAULT { $$=NULL; } + ; collation_name: - ident_or_text - { - if (!($$=get_charset_by_name($1.str,MYF(0)))) - { - my_error(ER_UNKNOWN_COLLATION, MYF(0), $1.str); - MYSQL_YYABORT; - } - }; + ident_or_text + { + if (!($$=get_charset_by_name($1.str,MYF(0)))) + { + my_error(ER_UNKNOWN_COLLATION, MYF(0), $1.str); + MYSQL_YYABORT; + } + } + ; opt_collate: - /* empty */ { $$=NULL; } - | COLLATE_SYM collation_name_or_default { $$=$2; } - ; + /* empty */ { $$=NULL; } + | COLLATE_SYM collation_name_or_default { $$=$2; } + ; collation_name_or_default: - collation_name { $$=$1; } - | DEFAULT { $$=NULL; } ; + collation_name { $$=$1; } + | DEFAULT { $$=NULL; } + ; opt_default: - /* empty */ {} - | DEFAULT {}; + /* empty */ {} + | DEFAULT {} + ; opt_binary: - /* empty */ { Lex->charset=NULL; } - | ASCII_SYM opt_bin_mod { Lex->charset=&my_charset_latin1; } - | BYTE_SYM { Lex->charset=&my_charset_bin; } - | UNICODE_SYM opt_bin_mod - { - if (!(Lex->charset=get_charset_by_csname("ucs2", - MY_CS_PRIMARY,MYF(0)))) - { - my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), "ucs2"); - MYSQL_YYABORT; - } - } - | charset charset_name opt_bin_mod { Lex->charset=$2; } - | BINARY opt_bin_charset { Lex->type|= BINCMP_FLAG; }; + /* empty */ { Lex->charset=NULL; } + | ASCII_SYM opt_bin_mod { Lex->charset=&my_charset_latin1; } + | BYTE_SYM { Lex->charset=&my_charset_bin; } + | UNICODE_SYM opt_bin_mod + { + if (!(Lex->charset=get_charset_by_csname("ucs2", + MY_CS_PRIMARY,MYF(0)))) + { + my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), "ucs2"); + MYSQL_YYABORT; + } + } + | charset charset_name opt_bin_mod { Lex->charset=$2; } + | BINARY opt_bin_charset { Lex->type|= BINCMP_FLAG; } + ; opt_bin_mod: - /* empty */ { } - | BINARY { Lex->type|= BINCMP_FLAG; }; + /* empty */ { } + | BINARY { Lex->type|= BINCMP_FLAG; } + ; opt_bin_charset: - /* empty */ { Lex->charset= NULL; } - | ASCII_SYM { Lex->charset=&my_charset_latin1; } - | UNICODE_SYM - { - if (!(Lex->charset=get_charset_by_csname("ucs2", - MY_CS_PRIMARY,MYF(0)))) - { - my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), "ucs2"); - MYSQL_YYABORT; - } - } - | charset charset_name { Lex->charset=$2; } ; + /* empty */ { Lex->charset= NULL; } + | ASCII_SYM { Lex->charset=&my_charset_latin1; } + | UNICODE_SYM + { + if (!(Lex->charset=get_charset_by_csname("ucs2", + MY_CS_PRIMARY,MYF(0)))) + { + my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), "ucs2"); + MYSQL_YYABORT; + } + } + | charset charset_name { Lex->charset=$2; } + ; opt_primary: - /* empty */ - | PRIMARY_SYM - ; + /* empty */ + | PRIMARY_SYM + ; references: - REFERENCES table_ident - { - LEX *lex=Lex; - lex->fk_delete_opt= lex->fk_update_opt= lex->fk_match_option= 0; - lex->ref_list.empty(); - } - opt_ref_list - { - $$=$2; - }; + REFERENCES table_ident + { + LEX *lex=Lex; + lex->fk_delete_opt= lex->fk_update_opt= lex->fk_match_option= 0; + lex->ref_list.empty(); + } + opt_ref_list + { + $$=$2; + } + ; opt_ref_list: - /* empty */ opt_on_delete {} - | '(' ref_list ')' opt_on_delete {}; + /* empty */ opt_on_delete {} + | '(' ref_list ')' opt_on_delete {} + ; ref_list: - ref_list ',' ident { Lex->ref_list.push_back(new Key_part_spec($3.str)); } - | ident { Lex->ref_list.push_back(new Key_part_spec($1.str)); }; - + ref_list ',' ident { Lex->ref_list.push_back(new Key_part_spec($3.str)); } + | ident { Lex->ref_list.push_back(new Key_part_spec($1.str)); } + ; opt_on_delete: - /* empty */ {} - | opt_on_delete_list {}; + /* empty */ {} + | opt_on_delete_list {} + ; opt_on_delete_list: - opt_on_delete_list opt_on_delete_item {} - | opt_on_delete_item {}; + opt_on_delete_list opt_on_delete_item {} + | opt_on_delete_item {} + ; opt_on_delete_item: - ON DELETE_SYM delete_option { Lex->fk_delete_opt= $3; } - | ON UPDATE_SYM delete_option { Lex->fk_update_opt= $3; } - | MATCH FULL { Lex->fk_match_option= Foreign_key::FK_MATCH_FULL; } - | MATCH PARTIAL { Lex->fk_match_option= Foreign_key::FK_MATCH_PARTIAL; } - | MATCH SIMPLE_SYM { Lex->fk_match_option= Foreign_key::FK_MATCH_SIMPLE; }; + ON DELETE_SYM delete_option { Lex->fk_delete_opt= $3; } + | ON UPDATE_SYM delete_option { Lex->fk_update_opt= $3; } + | MATCH FULL { Lex->fk_match_option= Foreign_key::FK_MATCH_FULL; } + | MATCH PARTIAL { Lex->fk_match_option= Foreign_key::FK_MATCH_PARTIAL; } + | MATCH SIMPLE_SYM { Lex->fk_match_option= Foreign_key::FK_MATCH_SIMPLE; } + ; delete_option: - RESTRICT { $$= (int) Foreign_key::FK_OPTION_RESTRICT; } - | CASCADE { $$= (int) Foreign_key::FK_OPTION_CASCADE; } - | SET NULL_SYM { $$= (int) Foreign_key::FK_OPTION_SET_NULL; } - | NO_SYM ACTION { $$= (int) Foreign_key::FK_OPTION_NO_ACTION; } - | SET DEFAULT { $$= (int) Foreign_key::FK_OPTION_DEFAULT; }; + RESTRICT { $$= (int) Foreign_key::FK_OPTION_RESTRICT; } + | CASCADE { $$= (int) Foreign_key::FK_OPTION_CASCADE; } + | SET NULL_SYM { $$= (int) Foreign_key::FK_OPTION_SET_NULL; } + | NO_SYM ACTION { $$= (int) Foreign_key::FK_OPTION_NO_ACTION; } + | SET DEFAULT { $$= (int) Foreign_key::FK_OPTION_DEFAULT; } + ; key_type: - key_or_index { $$= Key::MULTIPLE; } - | FULLTEXT_SYM opt_key_or_index { $$= Key::FULLTEXT; } - | SPATIAL_SYM opt_key_or_index - { + key_or_index { $$= Key::MULTIPLE; } + | FULLTEXT_SYM opt_key_or_index { $$= Key::FULLTEXT; } + | SPATIAL_SYM opt_key_or_index + { #ifdef HAVE_SPATIAL - $$= Key::SPATIAL; + $$= Key::SPATIAL; #else - my_error(ER_FEATURE_DISABLED, MYF(0), + my_error(ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name, sym_group_geom.needed_define); - MYSQL_YYABORT; + MYSQL_YYABORT; #endif - }; + } + ; constraint_key_type: - PRIMARY_SYM KEY_SYM { $$= Key::PRIMARY; } - | UNIQUE_SYM opt_key_or_index { $$= Key::UNIQUE; }; + PRIMARY_SYM KEY_SYM { $$= Key::PRIMARY; } + | UNIQUE_SYM opt_key_or_index { $$= Key::UNIQUE; } + ; key_or_index: - KEY_SYM {} - | INDEX_SYM {}; + KEY_SYM {} + | INDEX_SYM {} + ; opt_key_or_index: - /* empty */ {} - | key_or_index - ; + /* empty */ {} + | key_or_index + ; keys_or_index: - KEYS {} - | INDEX_SYM {} - | INDEXES {}; + KEYS {} + | INDEX_SYM {} + | INDEXES {} + ; opt_unique_or_fulltext: - /* empty */ { $$= Key::MULTIPLE; } - | UNIQUE_SYM { $$= Key::UNIQUE; } - | FULLTEXT_SYM { $$= Key::FULLTEXT;} - | SPATIAL_SYM - { + /* empty */ { $$= Key::MULTIPLE; } + | UNIQUE_SYM { $$= Key::UNIQUE; } + | FULLTEXT_SYM { $$= Key::FULLTEXT;} + | SPATIAL_SYM + { #ifdef HAVE_SPATIAL - $$= Key::SPATIAL; + $$= Key::SPATIAL; #else my_error(ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name, sym_group_geom.needed_define); - MYSQL_YYABORT; + MYSQL_YYABORT; #endif - } + } ; init_key_options: - { - Lex->key_create_info= default_key_create_info; - } - ; + { + Lex->key_create_info= default_key_create_info; + } + ; /* For now, key_alg initializies lex->key_create_info. @@ -5042,30 +5296,30 @@ init_key_options: */ key_alg: - /* empty */ init_key_options - | init_key_options key_using_alg - ; + init_key_options + | init_key_options key_using_alg + ; key_options: - /* empty */ {} - | key_opts - ; + /* empty */ {} + | key_opts + ; key_opts: - key_opt - | key_opts key_opt - ; + key_opt + | key_opts key_opt + ; key_using_alg: - USING btree_or_rtree { Lex->key_create_info.algorithm= $2; } - | TYPE_SYM btree_or_rtree { Lex->key_create_info.algorithm= $2; } + USING btree_or_rtree { Lex->key_create_info.algorithm= $2; } + | TYPE_SYM btree_or_rtree { Lex->key_create_info.algorithm= $2; } ; key_opt: - key_using_alg - | KEY_BLOCK_SIZE opt_equal ulong_num - { Lex->key_create_info.block_size= $3; } - | WITH PARSER_SYM IDENT_sys + key_using_alg + | KEY_BLOCK_SIZE opt_equal ulong_num + { Lex->key_create_info.block_size= $3; } + | WITH PARSER_SYM IDENT_sys { if (plugin_is_ready(&$3, MYSQL_FTPARSER_PLUGIN)) Lex->key_create_info.parser_name= $3; @@ -5077,127 +5331,127 @@ key_opt: } ; - btree_or_rtree: - BTREE_SYM { $$= HA_KEY_ALG_BTREE; } - | RTREE_SYM - { - $$= HA_KEY_ALG_RTREE; - } - | HASH_SYM { $$= HA_KEY_ALG_HASH; }; + BTREE_SYM { $$= HA_KEY_ALG_BTREE; } + | RTREE_SYM { $$= HA_KEY_ALG_RTREE; } + | HASH_SYM { $$= HA_KEY_ALG_HASH; } + ; key_list: - key_list ',' key_part order_dir { Lex->col_list.push_back($3); } - | key_part order_dir { Lex->col_list.push_back($1); }; + key_list ',' key_part order_dir { Lex->col_list.push_back($3); } + | key_part order_dir { Lex->col_list.push_back($1); } + ; key_part: - ident { $$=new Key_part_spec($1.str); } - | ident '(' NUM ')' - { - int key_part_len= atoi($3.str); - if (!key_part_len) + ident { $$=new Key_part_spec($1.str); } + | ident '(' NUM ')' { - my_error(ER_KEY_PART_0, MYF(0), $1.str); + int key_part_len= atoi($3.str); + if (!key_part_len) + { + my_error(ER_KEY_PART_0, MYF(0), $1.str); + } + $$=new Key_part_spec($1.str,(uint) key_part_len); } - $$=new Key_part_spec($1.str,(uint) key_part_len); - }; + ; opt_ident: - /* empty */ { $$=(char*) 0; } /* Defaultlength */ - | field_ident { $$=$1.str; }; + /* empty */ { $$=(char*) 0; /* Default length */ } + | field_ident { $$=$1.str; } + ; opt_component: - /* empty */ { $$= null_lex_str; } - | '.' ident { $$= $2; }; + /* empty */ { $$= null_lex_str; } + | '.' ident { $$= $2; } + ; string_list: - text_string { Lex->interval_list.push_back($1); } - | string_list ',' text_string { Lex->interval_list.push_back($3); }; + text_string { Lex->interval_list.push_back($1); } + | string_list ',' text_string { Lex->interval_list.push_back($3); }; /* ** Alter table */ alter: - ALTER opt_ignore TABLE_SYM table_ident - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - 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)) - MYSQL_YYABORT; - lex->alter_info.reset(); - lex->col_list.empty(); - lex->select_lex.init_order(); - lex->select_lex.db= - ((TABLE_LIST*) lex->select_lex.table_list.first)->db; - bzero((char*) &lex->create_info,sizeof(lex->create_info)); - lex->create_info.db_type= 0; - lex->create_info.default_table_charset= NULL; - lex->create_info.row_type= ROW_TYPE_NOT_USED; - lex->alter_info.reset(); - lex->no_write_to_binlog= 0; - lex->create_info.storage_media= HA_SM_DEFAULT; - } - alter_commands - {} - | ALTER DATABASE ident_or_empty + ALTER opt_ignore TABLE_SYM table_ident + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + 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)) + MYSQL_YYABORT; + lex->alter_info.reset(); + lex->col_list.empty(); + lex->select_lex.init_order(); + lex->select_lex.db= + ((TABLE_LIST*) lex->select_lex.table_list.first)->db; + bzero((char*) &lex->create_info,sizeof(lex->create_info)); + lex->create_info.db_type= 0; + lex->create_info.default_table_charset= NULL; + lex->create_info.row_type= ROW_TYPE_NOT_USED; + lex->alter_info.reset(); + lex->no_write_to_binlog= 0; + lex->create_info.storage_media= HA_SM_DEFAULT; + } + alter_commands + {} + | ALTER DATABASE ident_or_empty { Lex->create_info.default_table_charset= NULL; Lex->create_info.used_fields= 0; } - opt_create_database_options - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - lex->sql_command=SQLCOM_ALTER_DB; - lex->name= $3; + create_database_options + { + LEX *lex=Lex; + lex->sql_command=SQLCOM_ALTER_DB; + lex->name= $3; if (lex->name.str == NULL && - thd->copy_db_to(&lex->name.str, &lex->name.length)) - MYSQL_YYABORT; - } - | ALTER PROCEDURE sp_name - { - LEX *lex= Lex; - - if (lex->sphead) - { - my_error(ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE"); - MYSQL_YYABORT; - } - bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); - } - sp_a_chistics - { - LEX *lex=Lex; - - lex->sql_command= SQLCOM_ALTER_PROCEDURE; - lex->spname= $3; - } - | ALTER FUNCTION_SYM sp_name - { - LEX *lex= Lex; - - if (lex->sphead) - { - my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"); - MYSQL_YYABORT; - } - bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); - } - sp_a_chistics - { - LEX *lex=Lex; - - lex->sql_command= SQLCOM_ALTER_FUNCTION; - lex->spname= $3; - } + lex->copy_db_to(&lex->name.str, &lex->name.length)) + MYSQL_YYABORT; + } + | ALTER PROCEDURE sp_name + { + LEX *lex= Lex; + + if (lex->sphead) + { + my_error(ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE"); + MYSQL_YYABORT; + } + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + } + sp_a_chistics + { + LEX *lex=Lex; + + lex->sql_command= SQLCOM_ALTER_PROCEDURE; + lex->spname= $3; + } + | ALTER FUNCTION_SYM sp_name + { + LEX *lex= Lex; + + if (lex->sphead) + { + my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"); + MYSQL_YYABORT; + } + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + } + sp_a_chistics + { + LEX *lex=Lex; + + lex->sql_command= SQLCOM_ALTER_FUNCTION; + lex->spname= $3; + } | ALTER view_algorithm definer - { + { LEX *lex= Lex; if (lex->sphead) @@ -5224,11 +5478,11 @@ alter: MYSQL_YYABORT; } lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; - lex->create_view_mode= VIEW_ALTER; - } + lex->create_view_mode= VIEW_ALTER; + } view_tail - {} - | ALTER definer 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 @@ -5273,7 +5527,7 @@ alter: if (!($6 || $7 || $8 || $9 || $10)) { - my_parse_error(ER(ER_SYNTAX_ERROR)); + my_parse_error(ER(ER_SYNTAX_ERROR)); MYSQL_YYABORT; } /* @@ -5309,15 +5563,17 @@ alter: lex->server_options.server_name= $3.str; lex->server_options.server_name_length= $3.length; } - ; + ; -ev_alter_on_schedule_completion: /* empty */ { $$= 0;} +ev_alter_on_schedule_completion: + /* empty */ { $$= 0;} | ON SCHEDULE_SYM ev_schedule_time { $$= 1; } | ev_on_completion { $$= 1; } | ON SCHEDULE_SYM ev_schedule_time ev_on_completion { $$= 1; } - ; + ; -opt_ev_rename_to: /* empty */ { $$= 0;} +opt_ev_rename_to: + /* empty */ { $$= 0;} | RENAME TO_SYM sp_name { /* @@ -5327,19 +5583,22 @@ opt_ev_rename_to: /* empty */ { $$= 0;} Lex->spname= $3; $$= 1; } - ; + ; -opt_ev_sql_stmt: /* empty*/ { $$= 0;} +opt_ev_sql_stmt: + /* empty*/ { $$= 0;} | DO_SYM ev_sql_stmt { $$= 1; } ; ident_or_empty: - /* empty */ { $$.str= 0; $$.length= 0; } - | ident { $$= $1; }; + /* empty */ { $$.str= 0; $$.length= 0; } + | ident { $$= $1; } + ; alter_commands: - | DISCARD TABLESPACE { Lex->alter_info.tablespace_op= DISCARD_TABLESPACE; } - | IMPORT TABLESPACE { Lex->alter_info.tablespace_op= IMPORT_TABLESPACE; } + /* empty */ + | DISCARD TABLESPACE { Lex->alter_info.tablespace_op= DISCARD_TABLESPACE; } + | IMPORT TABLESPACE { Lex->alter_info.tablespace_op= IMPORT_TABLESPACE; } | alter_list opt_partitioning | alter_list @@ -5357,20 +5616,20 @@ alter_commands: | add_partition_rule | DROP PARTITION_SYM alt_part_name_list { - Lex->alter_info.flags|= ALTER_DROP_PARTITION; + Lex->alter_info.flags|= ALTER_DROP_PARTITION; } | REBUILD_SYM PARTITION_SYM opt_no_write_to_binlog all_or_alt_part_name_list { LEX *lex= Lex; - lex->alter_info.flags|= ALTER_REBUILD_PARTITION; + lex->alter_info.flags|= ALTER_REBUILD_PARTITION; lex->no_write_to_binlog= $3; } | OPTIMIZE PARTITION_SYM opt_no_write_to_binlog all_or_alt_part_name_list { LEX *lex= Lex; - lex->alter_info.flags|= ALTER_OPTIMIZE_PARTITION; + lex->alter_info.flags|= ALTER_OPTIMIZE_PARTITION; lex->no_write_to_binlog= $3; lex->check_opt.init(); } @@ -5379,7 +5638,7 @@ alter_commands: all_or_alt_part_name_list { LEX *lex= Lex; - lex->alter_info.flags|= ALTER_ANALYZE_PARTITION; + lex->alter_info.flags|= ALTER_ANALYZE_PARTITION; lex->no_write_to_binlog= $3; lex->check_opt.init(); } @@ -5387,7 +5646,7 @@ alter_commands: | CHECK_SYM PARTITION_SYM all_or_alt_part_name_list { LEX *lex= Lex; - lex->alter_info.flags|= ALTER_CHECK_PARTITION; + lex->alter_info.flags|= ALTER_CHECK_PARTITION; lex->check_opt.init(); } opt_mi_check_type @@ -5395,7 +5654,7 @@ alter_commands: all_or_alt_part_name_list { LEX *lex= Lex; - lex->alter_info.flags|= ALTER_REPAIR_PARTITION; + lex->alter_info.flags|= ALTER_REPAIR_PARTITION; lex->no_write_to_binlog= $3; lex->check_opt.init(); } @@ -5403,104 +5662,104 @@ alter_commands: | COALESCE PARTITION_SYM opt_no_write_to_binlog real_ulong_num { LEX *lex= Lex; - lex->alter_info.flags|= ALTER_COALESCE_PARTITION; + lex->alter_info.flags|= ALTER_COALESCE_PARTITION; lex->no_write_to_binlog= $3; - lex->alter_info.no_parts= $4; + lex->alter_info.no_parts= $4; } | reorg_partition_rule ; remove_partitioning: - REMOVE_SYM PARTITIONING_SYM - { - Lex->alter_info.flags|= ALTER_REMOVE_PARTITIONING; - } + REMOVE_SYM PARTITIONING_SYM + { + Lex->alter_info.flags|= ALTER_REMOVE_PARTITIONING; + } ; all_or_alt_part_name_list: - ALL - { - Lex->alter_info.flags|= ALTER_ALL_PARTITION; - } + ALL + { + Lex->alter_info.flags|= ALTER_ALL_PARTITION; + } | alt_part_name_list ; add_partition_rule: - ADD PARTITION_SYM opt_no_write_to_binlog - { - LEX *lex= Lex; - lex->part_info= new partition_info(); - if (!lex->part_info) + ADD PARTITION_SYM opt_no_write_to_binlog { - mem_alloc_error(sizeof(partition_info)); - MYSQL_YYABORT; + LEX *lex= Lex; + lex->part_info= new partition_info(); + if (!lex->part_info) + { + mem_alloc_error(sizeof(partition_info)); + MYSQL_YYABORT; + } + lex->alter_info.flags|= ALTER_ADD_PARTITION; + lex->no_write_to_binlog= $3; } - lex->alter_info.flags|= ALTER_ADD_PARTITION; - lex->no_write_to_binlog= $3; - } - add_part_extra - {} + add_part_extra + {} ; add_part_extra: + /* empty */ | '(' part_def_list ')' - { - LEX *lex= Lex; - lex->part_info->no_parts= lex->part_info->partitions.elements; - } + { + LEX *lex= Lex; + lex->part_info->no_parts= lex->part_info->partitions.elements; + } | PARTITIONS_SYM real_ulong_num - { - LEX *lex= Lex; - lex->part_info->no_parts= $2; - } + { + LEX *lex= Lex; + lex->part_info->no_parts= $2; + } ; reorg_partition_rule: - REORGANIZE_SYM PARTITION_SYM opt_no_write_to_binlog - { - LEX *lex= Lex; - lex->part_info= new partition_info(); - if (!lex->part_info) + REORGANIZE_SYM PARTITION_SYM opt_no_write_to_binlog { + LEX *lex= Lex; + lex->part_info= new partition_info(); + if (!lex->part_info) + { mem_alloc_error(sizeof(partition_info)); - MYSQL_YYABORT; + MYSQL_YYABORT; + } + lex->no_write_to_binlog= $3; } - lex->no_write_to_binlog= $3; - } - reorg_parts_rule + reorg_parts_rule ; reorg_parts_rule: - /* empty */ - { - Lex->alter_info.flags|= ALTER_TABLE_REORG; - } - | - alt_part_name_list - { - Lex->alter_info.flags|= ALTER_REORGANIZE_PARTITION; - } - INTO '(' part_def_list ')' - { - LEX *lex= Lex; - lex->part_info->no_parts= lex->part_info->partitions.elements; - } + /* empty */ + { + Lex->alter_info.flags|= ALTER_TABLE_REORG; + } + | alt_part_name_list + { + Lex->alter_info.flags|= ALTER_REORGANIZE_PARTITION; + } + INTO '(' part_def_list ')' + { + LEX *lex= Lex; + lex->part_info->no_parts= lex->part_info->partitions.elements; + } ; alt_part_name_list: - alt_part_name_item {} + alt_part_name_item {} | alt_part_name_list ',' alt_part_name_item {} ; alt_part_name_item: - ident - { - if (Lex->alter_info.partition_names.push_back($1.str)) + ident { - mem_alloc_error(1); - MYSQL_YYABORT; + if (Lex->alter_info.partition_names.push_back($1.str)) + { + mem_alloc_error(1); + MYSQL_YYABORT; + } } - } ; /* @@ -5508,34 +5767,35 @@ alt_part_name_item: */ alter_list: - alter_list_item - | alter_list ',' alter_list_item + alter_list_item + | alter_list ',' alter_list_item ; add_column: - ADD opt_column - { - LEX *lex=Lex; - lex->change=0; - lex->alter_info.flags|= ALTER_ADD_COLUMN; - }; + ADD opt_column + { + LEX *lex=Lex; + lex->change=0; + lex->alter_info.flags|= ALTER_ADD_COLUMN; + } + ; alter_list_item: - add_column column_def opt_place { } - | ADD key_def - { - Lex->alter_info.flags|= ALTER_ADD_INDEX; - } - | add_column '(' field_list ')' - { - Lex->alter_info.flags|= ALTER_ADD_COLUMN | ALTER_ADD_INDEX; - } - | CHANGE opt_column field_ident - { - LEX *lex=Lex; - lex->change= $3.str; - lex->alter_info.flags|= ALTER_CHANGE_COLUMN; - } + add_column column_def opt_place { } + | ADD key_def + { + Lex->alter_info.flags|= ALTER_ADD_INDEX; + } + | add_column '(' field_list ')' + { + Lex->alter_info.flags|= ALTER_ADD_COLUMN | ALTER_ADD_INDEX; + } + | CHANGE opt_column field_ident + { + LEX *lex=Lex; + lex->change= $3.str; + lex->alter_info.flags|= ALTER_CHANGE_COLUMN; + } field_spec opt_place | MODIFY_SYM opt_column field_ident { @@ -5543,8 +5803,8 @@ alter_list_item: lex->length=lex->dec=0; lex->type=0; lex->default_value= lex->on_update_value= 0; lex->comment=null_lex_str; - lex->charset= NULL; - lex->alter_info.flags|= ALTER_CHANGE_COLUMN; + lex->charset= NULL; + lex->alter_info.flags|= ALTER_CHANGE_COLUMN; } type opt_attribute { @@ -5554,69 +5814,68 @@ alter_list_item: lex->length,lex->dec,lex->type, lex->default_value, lex->on_update_value, &lex->comment, - $3.str, &lex->interval_list, lex->charset, - lex->uint_geom_type)) - MYSQL_YYABORT; + $3.str, &lex->interval_list, lex->charset, + lex->uint_geom_type)) + MYSQL_YYABORT; } opt_place - | DROP opt_column field_ident opt_restrict - { - LEX *lex=Lex; - lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::COLUMN, + | DROP opt_column field_ident opt_restrict + { + LEX *lex=Lex; + lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::COLUMN, $3.str)); - lex->alter_info.flags|= ALTER_DROP_COLUMN; - } - | DROP FOREIGN KEY_SYM opt_ident - { - Lex->alter_info.flags|= ALTER_DROP_INDEX | ALTER_FOREIGN_KEY; - } - | DROP PRIMARY_SYM KEY_SYM - { - LEX *lex=Lex; - lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::KEY, - primary_key_name)); - lex->alter_info.flags|= ALTER_DROP_INDEX; - } - | DROP key_or_index field_ident - { - LEX *lex=Lex; - lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::KEY, - $3.str)); - lex->alter_info.flags|= ALTER_DROP_INDEX; - } - | DISABLE_SYM KEYS - { - LEX *lex=Lex; + lex->alter_info.flags|= ALTER_DROP_COLUMN; + } + | DROP FOREIGN KEY_SYM opt_ident + { + Lex->alter_info.flags|= ALTER_DROP_INDEX | ALTER_FOREIGN_KEY; + } + | DROP PRIMARY_SYM KEY_SYM + { + LEX *lex=Lex; + lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::KEY, + primary_key_name)); + lex->alter_info.flags|= ALTER_DROP_INDEX; + } + | DROP key_or_index field_ident + { + LEX *lex=Lex; + lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::KEY, + $3.str)); + lex->alter_info.flags|= ALTER_DROP_INDEX; + } + | DISABLE_SYM KEYS + { + LEX *lex=Lex; lex->alter_info.keys_onoff= DISABLE; - lex->alter_info.flags|= ALTER_KEYS_ONOFF; + lex->alter_info.flags|= ALTER_KEYS_ONOFF; } - | ENABLE_SYM KEYS + | ENABLE_SYM KEYS { - LEX *lex=Lex; + LEX *lex=Lex; lex->alter_info.keys_onoff= ENABLE; - lex->alter_info.flags|= ALTER_KEYS_ONOFF; - } - | ALTER opt_column field_ident SET DEFAULT signed_literal - { - LEX *lex=Lex; - lex->alter_info.alter_list.push_back(new Alter_column($3.str,$6)); - lex->alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT; - } - | ALTER opt_column field_ident DROP DEFAULT - { - LEX *lex=Lex; - lex->alter_info.alter_list.push_back(new Alter_column($3.str, + lex->alter_info.flags|= ALTER_KEYS_ONOFF; + } + | ALTER opt_column field_ident SET DEFAULT signed_literal + { + LEX *lex=Lex; + lex->alter_info.alter_list.push_back(new Alter_column($3.str,$6)); + lex->alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT; + } + | ALTER opt_column field_ident DROP DEFAULT + { + LEX *lex=Lex; + lex->alter_info.alter_list.push_back(new Alter_column($3.str, (Item*) 0)); - lex->alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT; - } - | RENAME opt_to table_ident - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - size_t dummy; - lex->select_lex.db=$3->db.str; + lex->alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT; + } + | RENAME opt_to table_ident + { + LEX *lex=Lex; + size_t dummy; + lex->select_lex.db=$3->db.str; if (lex->select_lex.db == NULL && - thd->copy_db_to(&lex->select_lex.db, &dummy)) + lex->copy_db_to(&lex->select_lex.db, &dummy)) { MYSQL_YYABORT; } @@ -5626,150 +5885,153 @@ alter_list_item: my_error(ER_WRONG_TABLE_NAME, MYF(0), $3->table.str); MYSQL_YYABORT; } - lex->name= $3->table; - lex->alter_info.flags|= ALTER_RENAME; - } - | CONVERT_SYM TO_SYM charset charset_name_or_default opt_collate - { - if (!$4) - { - THD *thd= YYTHD; - $4= thd->variables.collation_database; - } - $5= $5 ? $5 : $4; - if (!my_charset_same($4,$5)) - { - my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), + lex->name= $3->table; + lex->alter_info.flags|= ALTER_RENAME; + } + | CONVERT_SYM TO_SYM charset charset_name_or_default opt_collate + { + if (!$4) + { + THD *thd= YYTHD; + $4= thd->variables.collation_database; + } + $5= $5 ? $5 : $4; + if (!my_charset_same($4,$5)) + { + my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), $5->name, $4->csname); - MYSQL_YYABORT; - } - LEX *lex= Lex; - lex->create_info.table_charset= - lex->create_info.default_table_charset= $5; - lex->create_info.used_fields|= (HA_CREATE_USED_CHARSET | - HA_CREATE_USED_DEFAULT_CHARSET); - lex->alter_info.flags|= ALTER_CONVERT; - } + MYSQL_YYABORT; + } + LEX *lex= Lex; + lex->create_info.table_charset= + lex->create_info.default_table_charset= $5; + lex->create_info.used_fields|= (HA_CREATE_USED_CHARSET | + HA_CREATE_USED_DEFAULT_CHARSET); + lex->alter_info.flags|= ALTER_CONVERT; + } | create_table_options_space_separated - { - LEX *lex=Lex; - lex->alter_info.flags|= ALTER_OPTIONS; - } - | FORCE_SYM - { - Lex->alter_info.flags|= ALTER_FORCE; - } - | alter_order_clause - { - LEX *lex=Lex; - lex->alter_info.flags|= ALTER_ORDER; - }; + { + LEX *lex=Lex; + lex->alter_info.flags|= ALTER_OPTIONS; + } + | FORCE_SYM + { + Lex->alter_info.flags|= ALTER_FORCE; + } + | alter_order_clause + { + LEX *lex=Lex; + lex->alter_info.flags|= ALTER_ORDER; + } + ; opt_column: - /* empty */ {} - | COLUMN_SYM {}; + /* empty */ {} + | COLUMN_SYM {} + ; opt_ignore: - /* empty */ { Lex->ignore= 0;} - | IGNORE_SYM { Lex->ignore= 1;} - ; + /* empty */ { Lex->ignore= 0;} + | IGNORE_SYM { Lex->ignore= 1;} + ; opt_restrict: - /* empty */ { Lex->drop_mode= DROP_DEFAULT; } - | RESTRICT { Lex->drop_mode= DROP_RESTRICT; } - | CASCADE { Lex->drop_mode= DROP_CASCADE; } - ; + /* empty */ { Lex->drop_mode= DROP_DEFAULT; } + | RESTRICT { Lex->drop_mode= DROP_RESTRICT; } + | CASCADE { Lex->drop_mode= DROP_CASCADE; } + ; opt_place: - /* empty */ {} - | AFTER_SYM ident { store_position_for_column($2.str); } - | FIRST_SYM { store_position_for_column(first_keyword); }; + /* empty */ {} + | AFTER_SYM ident { store_position_for_column($2.str); } + | FIRST_SYM { store_position_for_column(first_keyword); } + ; opt_to: - /* empty */ {} - | TO_SYM {} - | EQ {} - | AS {}; + /* empty */ {} + | TO_SYM {} + | EQ {} + | AS {} + ; /* SLAVE START and SLAVE STOP are deprecated. We keep them for compatibility. */ slave: - START_SYM SLAVE slave_thread_opts + START_SYM SLAVE slave_thread_opts { - LEX *lex=Lex; + LEX *lex=Lex; lex->sql_command = SQLCOM_SLAVE_START; - lex->type = 0; - /* We'll use mi structure for UNTIL options */ - bzero((char*) &lex->mi, sizeof(lex->mi)); + lex->type = 0; + /* We'll use mi structure for UNTIL options */ + bzero((char*) &lex->mi, sizeof(lex->mi)); /* If you change this code don't forget to update SLAVE START too */ } slave_until {} | STOP_SYM SLAVE slave_thread_opts { - LEX *lex=Lex; + LEX *lex=Lex; lex->sql_command = SQLCOM_SLAVE_STOP; - lex->type = 0; + lex->type = 0; /* If you change this code don't forget to update SLAVE STOP too */ } - | SLAVE START_SYM slave_thread_opts - { - LEX *lex=Lex; - lex->sql_command = SQLCOM_SLAVE_START; - lex->type = 0; - /* We'll use mi structure for UNTIL options */ - bzero((char*) &lex->mi, sizeof(lex->mi)); + | SLAVE START_SYM slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_START; + lex->type = 0; + /* We'll use mi structure for UNTIL options */ + bzero((char*) &lex->mi, sizeof(lex->mi)); } slave_until {} - | SLAVE STOP_SYM slave_thread_opts - { - LEX *lex=Lex; - lex->sql_command = SQLCOM_SLAVE_STOP; - lex->type = 0; - } + | SLAVE STOP_SYM slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_STOP; + lex->type = 0; + } ; - start: - START_SYM TRANSACTION_SYM start_transaction_opts - { - LEX *lex= Lex; - lex->sql_command= SQLCOM_BEGIN; - lex->start_transaction_opt= $3; - } - ; + START_SYM TRANSACTION_SYM start_transaction_opts + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_BEGIN; + lex->start_transaction_opt= $3; + } + ; start_transaction_opts: - /*empty*/ { $$ = 0; } + /*empty*/ { $$ = 0; } | WITH CONSISTENT_SYM SNAPSHOT_SYM - { - $$= MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT; - } + { + $$= MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT; + } ; slave_thread_opts: - { Lex->slave_thd_opt= 0; } - slave_thread_opt_list - {} - ; + { Lex->slave_thd_opt= 0; } + slave_thread_opt_list + {} + ; slave_thread_opt_list: - slave_thread_opt - | slave_thread_opt_list ',' slave_thread_opt - ; + slave_thread_opt + | slave_thread_opt_list ',' slave_thread_opt + ; slave_thread_opt: - /*empty*/ {} - | SQL_THREAD { Lex->slave_thd_opt|=SLAVE_SQL; } - | RELAY_THREAD { Lex->slave_thd_opt|=SLAVE_IO; } - ; + /*empty*/ {} + | SQL_THREAD { Lex->slave_thd_opt|=SLAVE_SQL; } + | RELAY_THREAD { Lex->slave_thd_opt|=SLAVE_IO; } + ; slave_until: - /*empty*/ {} - | UNTIL_SYM slave_until_opts + /*empty*/ {} + | UNTIL_SYM slave_until_opts { LEX *lex=Lex; if ((lex->mi.log_file_name || lex->mi.pos) && @@ -5781,173 +6043,180 @@ slave_until: ER(ER_BAD_SLAVE_UNTIL_COND), MYF(0)); MYSQL_YYABORT; } - } - ; + ; slave_until_opts: - master_file_def - | slave_until_opts ',' master_file_def ; - + master_file_def + | slave_until_opts ',' master_file_def + ; restore: - RESTORE_SYM table_or_tables - { - Lex->sql_command = SQLCOM_RESTORE_TABLE; - } - table_list FROM TEXT_STRING_sys - { - Lex->backup_dir = $6.str; - }; + RESTORE_SYM table_or_tables + { + Lex->sql_command = SQLCOM_RESTORE_TABLE; + } + table_list FROM TEXT_STRING_sys + { + Lex->backup_dir = $6.str; + } + ; backup: - BACKUP_SYM table_or_tables - { - Lex->sql_command = SQLCOM_BACKUP_TABLE; - } - table_list TO_SYM TEXT_STRING_sys - { - Lex->backup_dir = $6.str; - }; + BACKUP_SYM table_or_tables + { + Lex->sql_command = SQLCOM_BACKUP_TABLE; + } + table_list TO_SYM TEXT_STRING_sys + { + Lex->backup_dir = $6.str; + } + ; checksum: - CHECKSUM_SYM table_or_tables - { - LEX *lex=Lex; - lex->sql_command = SQLCOM_CHECKSUM; - } - table_list opt_checksum_type - {} - ; + CHECKSUM_SYM table_or_tables + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_CHECKSUM; + } + table_list opt_checksum_type + {} + ; opt_checksum_type: - /* nothing */ { Lex->check_opt.flags= 0; } - | QUICK { Lex->check_opt.flags= T_QUICK; } - | EXTENDED_SYM { Lex->check_opt.flags= T_EXTEND; } + /* nothing */ { Lex->check_opt.flags= 0; } + | QUICK { Lex->check_opt.flags= T_QUICK; } + | EXTENDED_SYM { Lex->check_opt.flags= T_EXTEND; } ; repair: - REPAIR opt_no_write_to_binlog table_or_tables - { - LEX *lex=Lex; - lex->sql_command = SQLCOM_REPAIR; - lex->no_write_to_binlog= $2; - lex->check_opt.init(); - } - table_list opt_mi_repair_type - {} - ; + REPAIR opt_no_write_to_binlog table_or_tables + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_REPAIR; + lex->no_write_to_binlog= $2; + lex->check_opt.init(); + } + table_list opt_mi_repair_type + {} + ; opt_mi_repair_type: - /* empty */ { Lex->check_opt.flags = T_MEDIUM; } - | mi_repair_types {}; + /* empty */ { Lex->check_opt.flags = T_MEDIUM; } + | mi_repair_types {} + ; mi_repair_types: - mi_repair_type {} - | mi_repair_type mi_repair_types {}; + mi_repair_type {} + | mi_repair_type mi_repair_types {} + ; mi_repair_type: - QUICK { Lex->check_opt.flags|= T_QUICK; } - | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; } - | USE_FRM { Lex->check_opt.sql_flags|= TT_USEFRM; }; + QUICK { Lex->check_opt.flags|= T_QUICK; } + | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; } + | USE_FRM { Lex->check_opt.sql_flags|= TT_USEFRM; } + ; analyze: - ANALYZE_SYM opt_no_write_to_binlog table_or_tables - { - LEX *lex=Lex; - lex->sql_command = SQLCOM_ANALYZE; - lex->no_write_to_binlog= $2; - lex->check_opt.init(); - } - table_list opt_mi_check_type - {} - ; + ANALYZE_SYM opt_no_write_to_binlog table_or_tables + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_ANALYZE; + lex->no_write_to_binlog= $2; + lex->check_opt.init(); + } + table_list opt_mi_check_type + {} + ; binlog_base64_event: - BINLOG_SYM TEXT_STRING_sys - { - Lex->sql_command = SQLCOM_BINLOG_BASE64_EVENT; - Lex->comment= $2; - } + BINLOG_SYM TEXT_STRING_sys + { + Lex->sql_command = SQLCOM_BINLOG_BASE64_EVENT; + Lex->comment= $2; + } ; check: - CHECK_SYM table_or_tables - { - LEX *lex=Lex; + CHECK_SYM table_or_tables + { + LEX *lex=Lex; - if (lex->sphead) - { - my_error(ER_SP_BADSTATEMENT, MYF(0), "CHECK"); - MYSQL_YYABORT; - } - lex->sql_command = SQLCOM_CHECK; - lex->check_opt.init(); - } - table_list opt_mi_check_type - {} - ; + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "CHECK"); + MYSQL_YYABORT; + } + lex->sql_command = SQLCOM_CHECK; + lex->check_opt.init(); + } + table_list opt_mi_check_type + {} + ; opt_mi_check_type: - /* empty */ { Lex->check_opt.flags = T_MEDIUM; } - | mi_check_types {}; + /* empty */ { Lex->check_opt.flags = T_MEDIUM; } + | mi_check_types {} + ; mi_check_types: - mi_check_type {} - | mi_check_type mi_check_types {}; + mi_check_type {} + | mi_check_type mi_check_types {} + ; mi_check_type: - QUICK { Lex->check_opt.flags|= T_QUICK; } - | FAST_SYM { Lex->check_opt.flags|= T_FAST; } - | MEDIUM_SYM { Lex->check_opt.flags|= T_MEDIUM; } - | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; } - | CHANGED { Lex->check_opt.flags|= T_CHECK_ONLY_CHANGED; } - | FOR_SYM UPGRADE_SYM { Lex->check_opt.sql_flags|= TT_FOR_UPGRADE; }; + QUICK { Lex->check_opt.flags|= T_QUICK; } + | FAST_SYM { Lex->check_opt.flags|= T_FAST; } + | MEDIUM_SYM { Lex->check_opt.flags|= T_MEDIUM; } + | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; } + | CHANGED { Lex->check_opt.flags|= T_CHECK_ONLY_CHANGED; } + | FOR_SYM UPGRADE_SYM { Lex->check_opt.sql_flags|= TT_FOR_UPGRADE; } + ; optimize: - OPTIMIZE opt_no_write_to_binlog table_or_tables - { - LEX *lex=Lex; - lex->sql_command = SQLCOM_OPTIMIZE; - lex->no_write_to_binlog= $2; - lex->check_opt.init(); - } - table_list opt_mi_check_type - {} - ; + OPTIMIZE opt_no_write_to_binlog table_or_tables + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_OPTIMIZE; + lex->no_write_to_binlog= $2; + lex->check_opt.init(); + } + table_list opt_mi_check_type + {} + ; opt_no_write_to_binlog: - /* empty */ { $$= 0; } - | NO_WRITE_TO_BINLOG { $$= 1; } - | LOCAL_SYM { $$= 1; } - ; + /* empty */ { $$= 0; } + | NO_WRITE_TO_BINLOG { $$= 1; } + | LOCAL_SYM { $$= 1; } + ; rename: - RENAME table_or_tables - { - Lex->sql_command= SQLCOM_RENAME_TABLE; - } - table_to_table_list - {} - | RENAME DATABASE + RENAME table_or_tables + { + Lex->sql_command= SQLCOM_RENAME_TABLE; + } + table_to_table_list + {} + | RENAME DATABASE { Lex->db_list.empty(); Lex->sql_command= SQLCOM_RENAME_DB; } db_to_db {} - | RENAME USER clear_privileges rename_list + | RENAME USER clear_privileges rename_list { - Lex->sql_command = SQLCOM_RENAME_USER; + Lex->sql_command = SQLCOM_RENAME_USER; } - ; + ; rename_list: - user TO_SYM user - { - if (Lex->users_list.push_back($1) || Lex->users_list.push_back($3)) - MYSQL_YYABORT; - } + user TO_SYM user + { + if (Lex->users_list.push_back($1) || Lex->users_list.push_back($3)) + MYSQL_YYABORT; + } | rename_list ',' user TO_SYM user { if (Lex->users_list.push_back($3) || Lex->users_list.push_back($5)) @@ -5956,103 +6225,108 @@ rename_list: ; table_to_table_list: - table_to_table - | table_to_table_list ',' table_to_table; + table_to_table + | table_to_table_list ',' table_to_table + ; table_to_table: - table_ident TO_SYM table_ident - { - LEX *lex=Lex; - SELECT_LEX *sl= lex->current_select; - if (!sl->add_table_to_list(lex->thd, $1,NULL,TL_OPTION_UPDATING, - TL_IGNORE) || - !sl->add_table_to_list(lex->thd, $3,NULL,TL_OPTION_UPDATING, - TL_IGNORE)) - MYSQL_YYABORT; - }; + table_ident TO_SYM table_ident + { + LEX *lex=Lex; + SELECT_LEX *sl= lex->current_select; + if (!sl->add_table_to_list(lex->thd, $1,NULL,TL_OPTION_UPDATING, + TL_IGNORE) || + !sl->add_table_to_list(lex->thd, $3,NULL,TL_OPTION_UPDATING, + TL_IGNORE)) + MYSQL_YYABORT; + } + ; db_to_db: - ident TO_SYM ident - { - LEX *lex=Lex; - if (lex->db_list.push_back((LEX_STRING*) - sql_memdup(&$1, sizeof(LEX_STRING))) || - lex->db_list.push_back((LEX_STRING*) - sql_memdup(&$3, sizeof(LEX_STRING)))) + ident TO_SYM ident + { + LEX *lex=Lex; + if (lex->db_list.push_back((LEX_STRING*) + sql_memdup(&$1, sizeof(LEX_STRING))) || + lex->db_list.push_back((LEX_STRING*) + sql_memdup(&$3, sizeof(LEX_STRING)))) MYSQL_YYABORT; - }; + } + ; keycache: - CACHE_SYM INDEX_SYM keycache_list IN_SYM key_cache_name - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_ASSIGN_TO_KEYCACHE; - lex->ident= $5; - } + CACHE_SYM INDEX_SYM keycache_list IN_SYM key_cache_name + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_ASSIGN_TO_KEYCACHE; + lex->ident= $5; + } ; keycache_list: - assign_to_keycache - | keycache_list ',' assign_to_keycache; + assign_to_keycache + | keycache_list ',' assign_to_keycache + ; assign_to_keycache: - table_ident cache_keys_spec - { - if (!Select->add_table_to_list(YYTHD, $1, NULL, 0, TL_READ, - Select->pop_index_hints())) - MYSQL_YYABORT; - } + table_ident cache_keys_spec + { + if (!Select->add_table_to_list(YYTHD, $1, NULL, 0, TL_READ, + Select->pop_index_hints())) + MYSQL_YYABORT; + } ; key_cache_name: - ident { $$= $1; } - | DEFAULT { $$ = default_key_cache_base; } - ; + ident { $$= $1; } + | DEFAULT { $$ = default_key_cache_base; } + ; preload: - LOAD INDEX_SYM INTO CACHE_SYM - { - LEX *lex=Lex; - lex->sql_command=SQLCOM_PRELOAD_KEYS; - } - preload_list - {} - ; + LOAD INDEX_SYM INTO CACHE_SYM + { + LEX *lex=Lex; + lex->sql_command=SQLCOM_PRELOAD_KEYS; + } + preload_list + {} + ; preload_list: - preload_keys - | preload_list ',' preload_keys; + preload_keys + | preload_list ',' preload_keys + ; preload_keys: - table_ident cache_keys_spec opt_ignore_leaves - { - if (!Select->add_table_to_list(YYTHD, $1, NULL, $3, TL_READ, - Select->pop_index_hints())) - MYSQL_YYABORT; - } - ; + table_ident cache_keys_spec opt_ignore_leaves + { + if (!Select->add_table_to_list(YYTHD, $1, NULL, $3, TL_READ, + Select->pop_index_hints())) + MYSQL_YYABORT; + } + ; cache_keys_spec: - { - Lex->select_lex.alloc_index_hints(YYTHD); - Select->set_index_hint_type(INDEX_HINT_USE, - global_system_variables.old_mode ? + { + Lex->select_lex.alloc_index_hints(YYTHD); + Select->set_index_hint_type(INDEX_HINT_USE, + global_system_variables.old_mode ? INDEX_HINT_MASK_JOIN : INDEX_HINT_MASK_ALL); - } - cache_key_list_or_empty + } + cache_key_list_or_empty ; cache_key_list_or_empty: - /* empty */ { } - | key_or_index '(' opt_key_usage_list ')' - ; + /* empty */ { } + | key_or_index '(' opt_key_usage_list ')' + ; opt_ignore_leaves: - /* empty */ - { $$= 0; } - | IGNORE_SYM LEAVES { $$= TL_OPTION_IGNORE_LEAVES; } - ; + /* empty */ + { $$= 0; } + | IGNORE_SYM LEAVES { $$= TL_OPTION_IGNORE_LEAVES; } + ; /* Select : retrieve data from table @@ -6060,29 +6334,29 @@ opt_ignore_leaves: select: - select_init - { - LEX *lex= Lex; - lex->sql_command= SQLCOM_SELECT; - } - ; + select_init + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SELECT; + } + ; /* Need select_init2 for subselects. */ select_init: - SELECT_SYM select_init2 - | - '(' select_paren ')' union_opt; + SELECT_SYM select_init2 + | '(' select_paren ')' union_opt + ; select_paren: - SELECT_SYM select_part2 - { - LEX *lex= Lex; + SELECT_SYM select_part2 + { + LEX *lex= Lex; SELECT_LEX * sel= lex->current_select; - if (sel->set_braces(1)) - { + if (sel->set_braces(1)) + { my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; - } + MYSQL_YYABORT; + } if (sel->linkage == UNION_TYPE && !sel->master_unit()->first_select()->braces && sel->master_unit()->first_select()->linkage == @@ -6092,56 +6366,59 @@ select_paren: MYSQL_YYABORT; } /* select in braces, can't contain global parameters */ - if (sel->master_unit()->fake_select_lex) + if (sel->master_unit()->fake_select_lex) sel->master_unit()->global_parameters= sel->master_unit()->fake_select_lex; } - | '(' select_paren ')'; + | '(' select_paren ')' + ; select_init2: - select_part2 - { - LEX *lex= Lex; - SELECT_LEX * sel= lex->current_select; - if (lex->current_select->set_braces(0)) - { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; - } - if (sel->linkage == UNION_TYPE && - sel->master_unit()->first_select()->braces) - { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; - } - } - union_clause - ; + select_part2 + { + LEX *lex= Lex; + SELECT_LEX * sel= lex->current_select; + if (lex->current_select->set_braces(0)) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } + if (sel->linkage == UNION_TYPE && + sel->master_unit()->first_select()->braces) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } + } + union_clause + ; select_part2: - { - LEX *lex= Lex; - SELECT_LEX *sel= lex->current_select; - if (sel->linkage != UNION_TYPE) - mysql_init_select(lex); - lex->current_select->parsing_place= SELECT_LIST; - } - select_options select_item_list - { - Select->parsing_place= NO_MATTER; - } - select_into select_lock_type; + { + LEX *lex= Lex; + SELECT_LEX *sel= lex->current_select; + if (sel->linkage != UNION_TYPE) + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; + } + select_options select_item_list + { + Select->parsing_place= NO_MATTER; + } + select_into select_lock_type + ; select_into: - opt_order_clause opt_limit_clause {} + opt_order_clause opt_limit_clause {} | into - | select_from - | into select_from - | select_from into; + | select_from + | into select_from + | select_from into + ; select_from: - FROM join_table_list where_clause group_clause having_clause - opt_order_clause opt_limit_clause procedure_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= @@ -6152,55 +6429,56 @@ select_from: and DUAL is system table without fields. Is "SELECT 1 FROM DUAL" any better than "SELECT 1" ? Hmmm :) */ - ; + ; select_options: - /* empty*/ - | select_option_list - { - if (Select->options & SELECT_DISTINCT && Select->options & SELECT_ALL) - { - my_error(ER_WRONG_USAGE, MYF(0), "ALL", "DISTINCT"); + /* empty*/ + | select_option_list + { + if (Select->options & SELECT_DISTINCT && Select->options & SELECT_ALL) + { + my_error(ER_WRONG_USAGE, MYF(0), "ALL", "DISTINCT"); MYSQL_YYABORT; - } + } } - ; + ; select_option_list: - select_option_list select_option - | select_option; + select_option_list select_option + | select_option + ; select_option: - STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; } - | HIGH_PRIORITY - { - if (check_simple_select()) - MYSQL_YYABORT; - Lex->lock_option= TL_READ_HIGH_PRIORITY; - } - | DISTINCT { Select->options|= SELECT_DISTINCT; } - | SQL_SMALL_RESULT { Select->options|= SELECT_SMALL_RESULT; } - | SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; } - | SQL_BUFFER_RESULT - { - if (check_simple_select()) - MYSQL_YYABORT; - Select->options|= OPTION_BUFFER_RESULT; - } - | SQL_CALC_FOUND_ROWS - { - if (check_simple_select()) - MYSQL_YYABORT; - Select->options|= OPTION_FOUND_ROWS; - } - | SQL_NO_CACHE_SYM + STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; } + | HIGH_PRIORITY + { + if (check_simple_select()) + MYSQL_YYABORT; + Lex->lock_option= TL_READ_HIGH_PRIORITY; + } + | DISTINCT { Select->options|= SELECT_DISTINCT; } + | SQL_SMALL_RESULT { Select->options|= SELECT_SMALL_RESULT; } + | SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; } + | SQL_BUFFER_RESULT + { + if (check_simple_select()) + MYSQL_YYABORT; + Select->options|= OPTION_BUFFER_RESULT; + } + | SQL_CALC_FOUND_ROWS + { + if (check_simple_select()) + MYSQL_YYABORT; + Select->options|= OPTION_FOUND_ROWS; + } + | SQL_NO_CACHE_SYM { Lex->safe_to_cache_query=0; - Lex->select_lex.options&= ~OPTION_TO_QUERY_CACHE; + Lex->select_lex.options&= ~OPTION_TO_QUERY_CACHE; Lex->select_lex.sql_cache= SELECT_LEX::SQL_NO_CACHE; } - | SQL_CACHE_SYM - { + | SQL_CACHE_SYM + { /* Honor this flag only if SQL_NO_CACHE wasn't specified AND we are parsing the outermost SELECT in the query. @@ -6209,179 +6487,233 @@ select_option: Lex->current_select == &Lex->select_lex) { Lex->safe_to_cache_query=1; - Lex->select_lex.options|= OPTION_TO_QUERY_CACHE; + Lex->select_lex.options|= OPTION_TO_QUERY_CACHE; Lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE; } - } - | ALL { Select->options|= SELECT_ALL; } - ; + } + | ALL { Select->options|= SELECT_ALL; } + ; select_lock_type: - /* empty */ - | FOR_SYM UPDATE_SYM - { - LEX *lex=Lex; - lex->current_select->set_lock_for_tables(TL_WRITE); - lex->safe_to_cache_query=0; - } - | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM - { - LEX *lex=Lex; - lex->current_select-> - set_lock_for_tables(TL_READ_WITH_SHARED_LOCKS); - lex->safe_to_cache_query=0; - } - ; + /* empty */ + | FOR_SYM UPDATE_SYM + { + LEX *lex=Lex; + lex->current_select->set_lock_for_tables(TL_WRITE); + lex->safe_to_cache_query=0; + } + | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM + { + LEX *lex=Lex; + lex->current_select-> + set_lock_for_tables(TL_READ_WITH_SHARED_LOCKS); + lex->safe_to_cache_query=0; + } + ; select_item_list: - select_item_list ',' select_item - | select_item - | '*' - { - THD *thd= YYTHD; - if (add_item_to_list(thd, + select_item_list ',' select_item + | select_item + | '*' + { + THD *thd= YYTHD; + if (add_item_to_list(thd, new Item_field(&thd->lex->current_select-> context, NULL, NULL, "*"))) - MYSQL_YYABORT; - (thd->lex->current_select->with_wild)++; - }; - + MYSQL_YYABORT; + (thd->lex->current_select->with_wild)++; + } + ; select_item: - remember_name select_item2 remember_end select_alias - { + remember_name select_item2 remember_end select_alias + { THD *thd= YYTHD; DBUG_ASSERT($1 < $3); - if (add_item_to_list(thd, $2)) - MYSQL_YYABORT; - if ($4.str) + if (add_item_to_list(thd, $2)) + MYSQL_YYABORT; + if ($4.str) { $2->is_autogenerated_name= FALSE; - $2->set_name($4.str, $4.length, system_charset_info); + $2->set_name($4.str, $4.length, system_charset_info); } - else if (!$2->name) + else if (!$2->name) { - $2->set_name($1, (uint) ($3 - $1), thd->charset()); - } - }; - + $2->set_name($1, (uint) ($3 - $1), thd->charset()); + } + } + ; remember_name: - { - THD *thd= YYTHD; - Lex_input_stream *lip= thd->m_lip; - $$= (char*) lip->get_cpp_tok_start(); - }; + { + THD *thd= YYTHD; + Lex_input_stream *lip= thd->m_lip; + $$= (char*) lip->get_cpp_tok_start(); + } + ; remember_end: - { - THD *thd= YYTHD; - Lex_input_stream *lip= thd->m_lip; - $$= (char*) lip->get_cpp_tok_end(); - }; + { + THD *thd= YYTHD; + Lex_input_stream *lip= thd->m_lip; + $$= (char*) lip->get_cpp_tok_end(); + } + ; select_item2: - table_wild { $$=$1; } /* table.* */ - | expr { $$=$1; }; + table_wild { $$=$1; /* table.* */ } + | expr { $$=$1; } + ; select_alias: - /* empty */ { $$=null_lex_str;} - | AS ident { $$=$2; } - | AS TEXT_STRING_sys { $$=$2; } - | ident { $$=$1; } - | TEXT_STRING_sys { $$=$1; } - ; + /* empty */ { $$=null_lex_str;} + | AS ident { $$=$2; } + | AS TEXT_STRING_sys { $$=$2; } + | ident { $$=$1; } + | TEXT_STRING_sys { $$=$1; } + ; optional_braces: - /* empty */ {} - | '(' ')' {}; + /* empty */ {} + | '(' ')' {} + ; /* all possible expressions */ -expr: - bool_term { Select->expr_list.push_front(new List<Item>); } - bool_or_expr +expr: + expr or expr %prec OR_SYM { - List<Item> *list= Select->expr_list.pop(); - if (list->elements) + /* + Design notes: + Do not use a manually maintained stack like thd->lex->xxx_list, + but use the internal bison stack ($$, $1 and $3) instead. + Using the bison stack is: + - more robust to changes in the grammar, + - guaranteed to be in sync with the parser state, + - better for performances (no memory allocation). + */ + Item_cond_or *item1; + Item_cond_or *item3; + if (is_cond_or($1)) { - list->push_front($1); - $$= new Item_cond_or(*list); - /* optimize construction of logical OR to reduce - amount of objects for complex expressions */ + item1= (Item_cond_or*) $1; + if (is_cond_or($3)) + { + item3= (Item_cond_or*) $3; + /* + (X1 OR X2) OR (Y1 OR Y2) ==> OR (X1, X2, Y1, Y2) + */ + item3->add_at_head(item1->argument_list()); + $$ = $3; + } + else + { + /* + (X1 OR X2) OR Y ==> OR (X1, X2, Y) + */ + item1->add($3); + $$ = $1; + } + } + else if (is_cond_or($3)) + { + item3= (Item_cond_or*) $3; + /* + X OR (Y1 OR Y2) ==> OR (X, Y1, Y2) + */ + item3->add_at_head($1); + $$ = $3; } else - $$= $1; - delete list; + { + /* X OR Y */ + $$ = new (YYTHD->mem_root) Item_cond_or($1, $3); + } } - ; - -bool_or_expr: - /* empty */ - | bool_or_expr or bool_term - { Select->expr_list.head()->push_back($3); } - ; - -bool_term: - bool_term XOR bool_term { $$= new Item_cond_xor($1,$3); } - | bool_factor { Select->expr_list.push_front(new List<Item>); } - bool_and_expr + | expr XOR expr %prec XOR { - List<Item> *list= Select->expr_list.pop(); - if (list->elements) + /* XOR is a proprietary extension */ + $$ = new (YYTHD->mem_root) Item_cond_xor($1, $3); + } + | expr and expr %prec AND_SYM + { + /* See comments in rule expr: expr or expr */ + Item_cond_and *item1; + Item_cond_and *item3; + if (is_cond_and($1)) + { + item1= (Item_cond_and*) $1; + if (is_cond_and($3)) + { + item3= (Item_cond_and*) $3; + /* + (X1 AND X2) AND (Y1 AND Y2) ==> AND (X1, X2, Y1, Y2) + */ + item3->add_at_head(item1->argument_list()); + $$ = $3; + } + else + { + /* + (X1 AND X2) AND Y ==> AND (X1, X2, Y) + */ + item1->add($3); + $$ = $1; + } + } + else if (is_cond_and($3)) { - list->push_front($1); - $$= new Item_cond_and(*list); - /* optimize construction of logical AND to reduce - amount of objects for complex expressions */ + item3= (Item_cond_and*) $3; + /* + X AND (Y1 AND Y2) ==> AND (X, Y1, Y2) + */ + item3->add_at_head($1); + $$ = $3; } else - $$= $1; - delete list; + { + /* X AND Y */ + $$ = new (YYTHD->mem_root) Item_cond_and($1, $3); + } } - ; - -bool_and_expr: - /* empty */ - | bool_and_expr and bool_factor - { Select->expr_list.head()->push_back($3); } - ; - -bool_factor: - NOT_SYM bool_factor { $$= negate_expression(YYTHD, $2); } - | bool_test ; - -bool_test: - bool_pri IS TRUE_SYM + | NOT_SYM expr %prec NOT_SYM + { $$= negate_expression(YYTHD, $2); } + | bool_pri IS TRUE_SYM %prec IS { $$= new (YYTHD->mem_root) Item_func_istrue($1); } - | bool_pri IS not TRUE_SYM + | bool_pri IS not TRUE_SYM %prec IS { $$= new (YYTHD->mem_root) Item_func_isnottrue($1); } - | bool_pri IS FALSE_SYM + | bool_pri IS FALSE_SYM %prec IS { $$= new (YYTHD->mem_root) Item_func_isfalse($1); } - | bool_pri IS not FALSE_SYM + | bool_pri IS not FALSE_SYM %prec IS { $$= 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 IS UNKNOWN_SYM %prec IS + { $$= new Item_func_isnull($1); } + | bool_pri IS not UNKNOWN_SYM %prec IS + { $$= new Item_func_isnotnull($1); } | bool_pri ; bool_pri: - bool_pri IS NULL_SYM { $$= new Item_func_isnull($1); } - | bool_pri IS not NULL_SYM { $$= new Item_func_isnotnull($1); } - | bool_pri EQUAL_SYM predicate { $$= new Item_func_equal($1,$3); } - | bool_pri comp_op predicate %prec EQ - { $$= (*$2)(0)->create($1,$3); } - | bool_pri comp_op all_or_any '(' subselect ')' %prec EQ - { $$= all_any_subquery_creator($1, $2, $3, $5); } - | predicate ; + bool_pri IS NULL_SYM %prec IS + { $$= new Item_func_isnull($1); } + | bool_pri IS not NULL_SYM %prec IS + { $$= new Item_func_isnotnull($1); } + | bool_pri EQUAL_SYM predicate %prec EQUAL_SYM + { $$= new Item_func_equal($1,$3); } + | bool_pri comp_op predicate %prec EQ + { $$= (*$2)(0)->create($1,$3); } + | bool_pri comp_op all_or_any '(' subselect ')' %prec EQ + { $$= all_any_subquery_creator($1, $2, $3, $5); } + | predicate ; + ; predicate: - bit_expr IN_SYM '(' subselect ')' + bit_expr IN_SYM '(' subselect ')' { $$= new (YYTHD->mem_root) Item_in_subselect($1, $4); } - | bit_expr not IN_SYM '(' subselect ')' + | bit_expr not IN_SYM '(' subselect ')' { THD *thd= YYTHD; Item *item= new (thd->mem_root) Item_in_subselect($1, $5); @@ -6391,148 +6723,165 @@ predicate: { $$= handle_sql2003_note184_exception(YYTHD, $1, true, $4); } - | bit_expr IN_SYM '(' expr ',' expr_list ')' - { - $6->push_front($4); - $6->push_front($1); + | 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 ')' { $$= 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); + $7->push_front($5); + $7->push_front($1); Item_func_in *item = new (YYTHD->mem_root) Item_func_in(*$7); - item->negate(); - $$= item; + item->negate(); + $$= item; } - | bit_expr BETWEEN_SYM bit_expr AND_SYM predicate - { $$= new Item_func_between($1,$3,$5); } - | bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate - { - Item_func_between *item= new Item_func_between($1,$4,$6); - item->negate(); - $$= item; - } - | bit_expr SOUNDS_SYM LIKE bit_expr - { $$= new Item_func_eq(new Item_func_soundex($1), - new Item_func_soundex($4)); } - | bit_expr LIKE simple_expr opt_escape + | bit_expr BETWEEN_SYM bit_expr AND_SYM predicate + { $$= new Item_func_between($1,$3,$5); } + | bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate + { + Item_func_between *item= new Item_func_between($1,$4,$6); + item->negate(); + $$= item; + } + | bit_expr SOUNDS_SYM LIKE bit_expr + { + $$= new Item_func_eq(new Item_func_soundex($1), + new Item_func_soundex($4)); + } + | bit_expr LIKE simple_expr opt_escape { $$= new Item_func_like($1,$3,$4,Lex->escape_used); } - | bit_expr not LIKE simple_expr opt_escape + | bit_expr not LIKE simple_expr opt_escape { $$= new Item_func_not(new Item_func_like($1,$4,$5, Lex->escape_used)); } - | bit_expr REGEXP bit_expr { $$= new Item_func_regex($1,$3); } - | bit_expr not REGEXP bit_expr + | bit_expr REGEXP bit_expr { $$= new Item_func_regex($1,$3); } + | bit_expr not REGEXP bit_expr { $$= negate_expression(YYTHD, new Item_func_regex($1,$4)); } - | bit_expr ; + | bit_expr + ; bit_expr: - bit_expr '|' bit_term { $$= new Item_func_bit_or($1,$3); } - | bit_term ; - -bit_term: - bit_term '&' bit_factor { $$= new Item_func_bit_and($1,$3); } - | bit_factor ; - -bit_factor: - bit_factor SHIFT_LEFT value_expr - { $$= new Item_func_shift_left($1,$3); } - | bit_factor SHIFT_RIGHT value_expr - { $$= new Item_func_shift_right($1,$3); } - | value_expr ; - -value_expr: - value_expr '+' term { $$= new Item_func_plus($1,$3); } - | value_expr '-' term { $$= new Item_func_minus($1,$3); } - | value_expr '+' interval_expr interval - { $$= new Item_date_add_interval($1,$3,$4,0); } - | value_expr '-' interval_expr interval - { $$= new Item_date_add_interval($1,$3,$4,1); } - | term ; - -term: - term '*' factor { $$= new Item_func_mul($1,$3); } - | term '/' factor { $$= new Item_func_div($1,$3); } - | term '%' factor { $$= new Item_func_mod($1,$3); } - | term DIV_SYM factor { $$= new Item_func_int_div($1,$3); } - | term MOD_SYM factor { $$= new Item_func_mod($1,$3); } - | factor ; - -factor: - factor '^' simple_expr { $$= new Item_func_bit_xor($1,$3); } - | simple_expr ; - -or: OR_SYM | OR2_SYM; -and: AND_SYM | AND_AND_SYM; -not: NOT_SYM | NOT2_SYM; -not2: '!' | NOT2_SYM; - -comp_op: EQ { $$ = &comp_eq_creator; } - | GE { $$ = &comp_ge_creator; } - | GT_SYM { $$ = &comp_gt_creator; } - | LE { $$ = &comp_le_creator; } - | LT { $$ = &comp_lt_creator; } - | NE { $$ = &comp_ne_creator; } - ; + bit_expr '|' bit_expr %prec '|' + { $$= new Item_func_bit_or($1,$3); } + | bit_expr '&' bit_expr %prec '&' + { $$= new Item_func_bit_and($1,$3); } + | bit_expr SHIFT_LEFT bit_expr %prec SHIFT_LEFT + { $$= new Item_func_shift_left($1,$3); } + | bit_expr SHIFT_RIGHT bit_expr %prec SHIFT_RIGHT + { $$= new Item_func_shift_right($1,$3); } + | bit_expr '+' bit_expr %prec '+' + { $$= new Item_func_plus($1,$3); } + | bit_expr '-' bit_expr %prec '-' + { $$= new Item_func_minus($1,$3); } + | bit_expr '+' interval_expr interval %prec '+' + { $$= new Item_date_add_interval($1,$3,$4,0); } + | bit_expr '-' interval_expr interval %prec '-' + { $$= new Item_date_add_interval($1,$3,$4,1); } + | bit_expr '*' bit_expr %prec '*' + { $$= new Item_func_mul($1,$3); } + | bit_expr '/' bit_expr %prec '/' + { $$= new Item_func_div($1,$3); } + | bit_expr '%' bit_expr %prec '%' + { $$= new Item_func_mod($1,$3); } + | bit_expr DIV_SYM bit_expr %prec DIV_SYM + { $$= new Item_func_int_div($1,$3); } + | bit_expr MOD_SYM bit_expr %prec MOD_SYM + { $$= new Item_func_mod($1,$3); } + | bit_expr '^' bit_expr + { $$= new Item_func_bit_xor($1,$3); } + | simple_expr + ; + +or: + OR_SYM + | OR2_SYM + ; + +and: + AND_SYM + | AND_AND_SYM + ; -all_or_any: ALL { $$ = 1; } - | ANY_SYM { $$ = 0; } +not: + NOT_SYM + | NOT2_SYM + ; + +not2: + '!' + | NOT2_SYM + ; + +comp_op: + EQ { $$ = &comp_eq_creator; } + | GE { $$ = &comp_ge_creator; } + | GT_SYM { $$ = &comp_gt_creator; } + | LE { $$ = &comp_le_creator; } + | LT { $$ = &comp_lt_creator; } + | NE { $$ = &comp_ne_creator; } + ; + +all_or_any: + ALL { $$ = 1; } + | ANY_SYM { $$ = 0; } ; interval_expr: - INTERVAL_SYM expr { $$=$2; } + INTERVAL_SYM expr %prec INTERVAL_SYM + { $$=$2; } ; simple_expr: - simple_ident + simple_ident | function_call_keyword | function_call_nonkeyword | function_call_generic | function_call_conflict - | simple_expr COLLATE_SYM ident_or_text %prec NEG - { + | simple_expr COLLATE_SYM ident_or_text %prec NEG + { 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 - | variable - | sum_expr - | simple_expr OR_OR_SYM simple_expr - { $$= new (YYTHD->mem_root) Item_func_concat($1, $3); } - | '+' simple_expr %prec NEG { $$= $2; } - | '-' simple_expr %prec NEG + $$= new (thd->mem_root) Item_func_set_collation($1, i1); + } + | literal + | param_marker + | variable + | sum_expr + | simple_expr OR_OR_SYM simple_expr + { $$= new (YYTHD->mem_root) Item_func_concat($1, $3); } + | '+' simple_expr %prec NEG { $$= $2; } + | '-' simple_expr %prec NEG { $$= new (YYTHD->mem_root) Item_func_neg($2); } - | '~' simple_expr %prec NEG + | '~' simple_expr %prec NEG { $$= new (YYTHD->mem_root) Item_func_bit_neg($2); } - | not2 simple_expr %prec NEG + | not2 simple_expr %prec NEG { $$= negate_expression(YYTHD, $2); } - | '(' subselect ')' + | '(' subselect ')' { $$= new (YYTHD->mem_root) Item_singlerow_subselect($2); } - | '(' expr ')' { $$= $2; } - | '(' expr ',' expr_list ')' - { - $4->push_front($2); - $$= new (YYTHD->mem_root) Item_row(*$4); - } - | ROW_SYM '(' expr ',' expr_list ')' - { - $5->push_front($3); - $$= new (YYTHD->mem_root) Item_row(*$5); - } - | EXISTS '(' subselect ')' + | '(' expr ')' { $$= $2; } + | '(' expr ',' expr_list ')' + { + $4->push_front($2); + $$= new (YYTHD->mem_root) Item_row(*$4); + } + | ROW_SYM '(' expr ',' expr_list ')' + { + $5->push_front($3); + $$= new (YYTHD->mem_root) Item_row(*$5); + } + | EXISTS '(' subselect ')' { $$= new (YYTHD->mem_root) Item_exists_subselect($3); } - | '{' ident expr '}' { $$= $3; } + | '{' ident expr '}' { $$= $3; } | MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')' { $2->push_front($5); @@ -6540,50 +6889,52 @@ simple_expr: Select->add_ftfunc_to_list(i1); $$= i1; } - | BINARY simple_expr %prec NEG - { + | BINARY simple_expr %prec NEG + { $$= create_func_cast(YYTHD, $2, ITEM_CAST_CHAR, NULL, NULL, &my_charset_bin); - } - | CAST_SYM '(' expr AS cast_type ')' - { + } + | CAST_SYM '(' expr AS cast_type ')' + { LEX *lex= Lex; - $$= create_func_cast(YYTHD, $3, $5, lex->length, lex->dec, + $$= create_func_cast(YYTHD, $3, $5, lex->length, lex->dec, lex->charset); if (!$$) MYSQL_YYABORT; - } - | 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(YYTHD, $3, $5, Lex->length, Lex->dec, + } + | 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(YYTHD, $3, $5, Lex->length, Lex->dec, Lex->charset); if (!$$) MYSQL_YYABORT; - } - | CONVERT_SYM '(' expr USING charset_name ')' - { $$= new (YYTHD->mem_root) Item_func_conv_charset($3,$5); } - | DEFAULT '(' simple_ident ')' - { - if ($3->is_splocal()) - { - Item_splocal *il= static_cast<Item_splocal *>($3); - - my_error(ER_WRONG_COLUMN_NAME, MYF(0), il->my_name()->str); - MYSQL_YYABORT; - } - $$= new (YYTHD->mem_root) Item_default_value(Lex->current_context(), + } + | CONVERT_SYM '(' expr USING charset_name ')' + { $$= new (YYTHD->mem_root) Item_func_conv_charset($3,$5); } + | DEFAULT '(' simple_ident ')' + { + if ($3->is_splocal()) + { + Item_splocal *il= static_cast<Item_splocal *>($3); + + my_error(ER_WRONG_COLUMN_NAME, MYF(0), il->my_name()->str); + MYSQL_YYABORT; + } + $$= new (YYTHD->mem_root) Item_default_value(Lex->current_context(), $3); - } - | VALUES '(' simple_ident_nospvar ')' - { $$= 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 - { + } + | VALUES '(' simple_ident_nospvar ')' + { + $$= 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->type() != Item::ROW_ITEM) { my_parse_error(ER(ER_SYNTAX_ERROR)); @@ -6600,62 +6951,62 @@ simple_expr: 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 (YYTHD->mem_root) Item_func_char(*$3, $5); } - | CURRENT_USER optional_braces + CHAR_SYM '(' expr_list ')' + { $$= new (YYTHD->mem_root) Item_func_char(*$3); } + | CHAR_SYM '(' expr_list USING charset_name ')' + { $$= new (YYTHD->mem_root) Item_func_char(*$3, $5); } + | CURRENT_USER optional_braces { $$= new (YYTHD->mem_root) Item_func_current_user(Lex->current_context()); Lex->safe_to_cache_query= 0; } - | DATE_SYM '(' expr ')' - { $$= new (YYTHD->mem_root) Item_date_typecast($3); } - | DAY_SYM '(' expr ')' - { $$= new (YYTHD->mem_root) Item_func_dayofmonth($3); } - | HOUR_SYM '(' expr ')' - { $$= new (YYTHD->mem_root) Item_func_hour($3); } - | INSERT '(' expr ',' expr ',' expr ',' 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 '(' ')' - { + | DATE_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_date_typecast($3); } + | DAY_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_dayofmonth($3); } + | HOUR_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_hour($3); } + | INSERT '(' expr ',' expr ',' expr ',' 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 '(' ')' + { $$= new (YYTHD->mem_root) Item_func_user(); Lex->safe_to_cache_query=0; } - | YEAR_SYM '(' expr ')' - { $$= new (YYTHD->mem_root) Item_func_year($3); } + | YEAR_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_year($3); } ; /* @@ -6671,64 +7022,64 @@ function_call_keyword: discouraged. */ function_call_nonkeyword: - ADDDATE_SYM '(' expr ',' expr ')' - { + ADDDATE_SYM '(' expr ',' 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 - { + | ADDDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')' + { $$= new (YYTHD->mem_root) Item_date_add_interval($3, $6, $7, 0); } + | CURDATE optional_braces + { $$= new (YYTHD->mem_root) Item_func_curdate_local(); Lex->safe_to_cache_query=0; } - | CURTIME optional_braces - { + | CURTIME optional_braces + { $$= 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; - } - | 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 - { + | CURTIME '(' expr ')' + { + $$= new (YYTHD->mem_root) Item_func_curtime_local($3); + Lex->safe_to_cache_query=0; + } + | 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 (YYTHD->mem_root) Item_func_now_local(); Lex->safe_to_cache_query=0; } - | NOW_SYM '(' expr ')' - { + | NOW_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_now_local($3); Lex->safe_to_cache_query=0; } - | POSITION_SYM '(' bit_expr IN_SYM expr ')' - { $$ = new (YYTHD->mem_root) Item_func_locate($5,$3); } - | SUBDATE_SYM '(' expr ',' expr ')' - { + | POSITION_SYM '(' bit_expr IN_SYM expr ')' + { $$ = new (YYTHD->mem_root) Item_func_locate($5,$3); } + | SUBDATE_SYM '(' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_date_add_interval($3, $5, INTERVAL_DAY, 1); } - | SUBDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')' - { $$= new (YYTHD->mem_root) Item_date_add_interval($3, $6, $7, 1); } - | SUBSTRING '(' expr ',' expr ',' expr ')' - { $$= new (YYTHD->mem_root) Item_func_substr($3,$5,$7); } - | SUBSTRING '(' expr ',' expr ')' - { $$= new (YYTHD->mem_root) Item_func_substr($3,$5); } - | SUBSTRING '(' expr FROM expr FOR_SYM expr ')' - { $$= new (YYTHD->mem_root) Item_func_substr($3,$5,$7); } - | SUBSTRING '(' expr FROM expr ')' - { $$= new (YYTHD->mem_root) Item_func_substr($3,$5); } - | SYSDATE optional_braces + | SUBDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')' + { $$= new (YYTHD->mem_root) Item_date_add_interval($3, $6, $7, 1); } + | SUBSTRING '(' expr ',' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_substr($3,$5,$7); } + | SUBSTRING '(' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_substr($3,$5); } + | SUBSTRING '(' expr FROM expr FOR_SYM expr ')' + { $$= new (YYTHD->mem_root) Item_func_substr($3,$5,$7); } + | SUBSTRING '(' expr FROM expr ')' + { $$= new (YYTHD->mem_root) Item_func_substr($3,$5); } + | SYSDATE optional_braces { if (global_system_variables.sysdate_is_now == 0) $$= new (YYTHD->mem_root) Item_func_sysdate_local(); @@ -6736,7 +7087,7 @@ function_call_nonkeyword: $$= new (YYTHD->mem_root) Item_func_now_local(); Lex->safe_to_cache_query=0; } - | SYSDATE '(' expr ')' + | SYSDATE '(' expr ')' { if (global_system_variables.sysdate_is_now == 0) $$= new (YYTHD->mem_root) Item_func_sysdate_local($3); @@ -6744,22 +7095,22 @@ function_call_nonkeyword: $$= new (YYTHD->mem_root) Item_func_now_local($3); Lex->safe_to_cache_query=0; } - | TIMESTAMP_ADD '(' interval_time_st ',' expr ',' expr ')' - { $$= new (YYTHD->mem_root) Item_date_add_interval($7,$5,$3,0); } - | TIMESTAMP_DIFF '(' interval_time_st ',' expr ',' expr ')' - { $$= new (YYTHD->mem_root) Item_func_timestamp_diff($5,$7,$3); } - | UTC_DATE_SYM optional_braces - { + | TIMESTAMP_ADD '(' interval_time_st ',' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_date_add_interval($7,$5,$3,0); } + | TIMESTAMP_DIFF '(' interval_time_st ',' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_timestamp_diff($5,$7,$3); } + | UTC_DATE_SYM optional_braces + { $$= new (YYTHD->mem_root) Item_func_curdate_utc(); Lex->safe_to_cache_query=0; } - | UTC_TIME_SYM optional_braces - { + | UTC_TIME_SYM optional_braces + { $$= new (YYTHD->mem_root) Item_func_curtime_utc(); Lex->safe_to_cache_query=0; } - | UTC_TIMESTAMP_SYM optional_braces - { + | UTC_TIMESTAMP_SYM optional_braces + { $$= new (YYTHD->mem_root) Item_func_now_utc(); Lex->safe_to_cache_query=0; } @@ -6771,29 +7122,29 @@ function_call_nonkeyword: a dedicated rule is needed here. */ function_call_conflict: - ASCII_SYM '(' expr ')' + 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(); + | 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; - } - | 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 ')' - { + } + | 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) @@ -6801,17 +7152,17 @@ function_call_conflict: 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 ')' - { + } + | 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, @@ -6819,8 +7170,8 @@ function_call_conflict: $$= new (thd->mem_root) Item_func_week($3, i1); } - | WEEK_SYM '(' expr ',' expr ')' - { $$= new (YYTHD->mem_root) Item_func_week($3,$5); } + | WEEK_SYM '(' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_week($3,$5); } | geometry_function { #ifdef HAVE_SPATIAL @@ -6834,54 +7185,54 @@ function_call_conflict: ; geometry_function: - CONTAINS_SYM '(' expr ',' expr ')' - { + CONTAINS_SYM '(' expr ',' expr ')' + { $$= GEOM_NEW(YYTHD, Item_func_spatial_rel($3, $5, Item_func::SP_CONTAINS_FUNC)); } - | GEOMETRYCOLLECTION '(' expr_list ')' - { + | GEOMETRYCOLLECTION '(' expr_list ')' + { $$= GEOM_NEW(YYTHD, Item_func_spatial_collection(* $3, Geometry::wkb_geometrycollection, Geometry::wkb_point)); } - | LINESTRING '(' expr_list ')' - { + | LINESTRING '(' expr_list ')' + { $$= GEOM_NEW(YYTHD, Item_func_spatial_collection(* $3, Geometry::wkb_linestring, Geometry::wkb_point)); } - | MULTILINESTRING '(' expr_list ')' - { + | MULTILINESTRING '(' expr_list ')' + { $$= GEOM_NEW(YYTHD, Item_func_spatial_collection(* $3, Geometry::wkb_multilinestring, Geometry::wkb_linestring)); } - | MULTIPOINT '(' expr_list ')' - { + | MULTIPOINT '(' expr_list ')' + { $$= GEOM_NEW(YYTHD, Item_func_spatial_collection(* $3, Geometry::wkb_multipoint, Geometry::wkb_point)); } - | MULTIPOLYGON '(' expr_list ')' - { + | 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 ')' - { + | 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_polygon, Geometry::wkb_linestring)); } ; @@ -6896,103 +7247,103 @@ geometry_function: in sql/item_create.cc */ function_call_generic: - IDENT_sys '(' - { -#ifdef HAVE_DLOPEN - udf_func *udf= 0; - LEX *lex= Lex; - if (using_udf_functions && - (udf= find_udf($1.str, $1.length)) && - udf->type == UDFTYPE_AGGREGATE) + IDENT_sys '(' { - if (lex->current_select->inc_in_sum_expr()) +#ifdef HAVE_DLOPEN + udf_func *udf= 0; + LEX *lex= Lex; + if (using_udf_functions && + (udf= find_udf($1.str, $1.length)) && + udf->type == UDFTYPE_AGGREGATE) { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; + if (lex->current_select->inc_in_sum_expr()) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } } - } - /* Temporary placing the result of find_udf in $3 */ - $<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); } - else + opt_udf_expr_list ')' { -#ifdef HAVE_DLOPEN - /* Retrieving the result of find_udf */ - udf_func *udf= $<udf>3; + THD *thd= YYTHD; + Create_func *builder; + Item *item= NULL; - if (udf) - { - if (udf->type == UDFTYPE_AGGREGATE) - { - Select->in_sum_expr--; - } + /* + Implementation note: + names are resolved with the following order: + - MySQL native functions, + - User Defined Functions, + - Stored Functions (assuming the current <use> database) - item= Create_udf_func::s_singleton.create(thd, udf, $4); + This will be revised with WL#2128 (SQL PATH) + */ + builder= find_native_function_builder(thd, $1); + if (builder) + { + item= builder->create(thd, $1, $4); } else + { +#ifdef HAVE_DLOPEN + /* Retrieving the result of find_udf */ + udf_func *udf= $<udf>3; + + if (udf) + { + if (udf->type == UDFTYPE_AGGREGATE) + { + Select->in_sum_expr--; + } + + item= Create_udf_func::s_singleton.create(thd, udf, $4); + } + else #endif + { + builder= find_qualified_function_builder(thd); + DBUG_ASSERT(builder); + item= builder->create(thd, $1, $4); + } + } + + if (! ($$= item)) { - builder= find_qualified_function_builder(thd); - DBUG_ASSERT(builder); - item= builder->create(thd, $1, $4); + MYSQL_YYABORT; } } - - if (! ($$= item)) + | ident '.' ident '(' opt_expr_list ')' { - MYSQL_YYABORT; - } - } - | ident '.' ident '(' opt_expr_list ')' - { - THD *thd= YYTHD; - Create_qfunc *builder; - Item *item= NULL; + THD *thd= YYTHD; + Create_qfunc *builder; + Item *item= NULL; - /* - 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). - */ + /* + 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, true, $5); + builder= find_qualified_function_builder(thd); + DBUG_ASSERT(builder); + item= builder->create(thd, $1, $3, true, $5); - if (! ($$= item)) - { - MYSQL_YYABORT; + if (! ($$= item)) + { + MYSQL_YYABORT; + } } - } ; fulltext_options: @@ -7003,115 +7354,112 @@ fulltext_options: ; opt_natural_language_mode: - /* nothing */ { $$= FT_NL; } + /* nothing */ { $$= FT_NL; } | IN_SYM NATURAL LANGUAGE_SYM MODE_SYM { $$= FT_NL; } ; opt_query_expansion: - /* nothing */ { $$= 0; } + /* nothing */ { $$= 0; } | WITH QUERY_SYM EXPANSION_SYM { $$= FT_EXPAND; } ; -udf_expr_list: - /* empty */ { $$= NULL; } - | udf_expr_list2 { $$= $1;} - ; - -udf_expr_list2: - { Select->expr_list.push_front(new List<Item>); } - udf_expr_list3 - { $$= Select->expr_list.pop(); } - ; +opt_udf_expr_list: + /* empty */ { $$= NULL; } + | udf_expr_list { $$= $1; } + ; -udf_expr_list3: - udf_expr - { - Select->expr_list.head()->push_back($1); - } - | udf_expr_list3 ',' udf_expr - { - Select->expr_list.head()->push_back($3); - } - ; +udf_expr_list: + udf_expr + { + $$= new (YYTHD->mem_root) List<Item>; + $$->push_back($1); + } + | udf_expr_list ',' udf_expr + { + $1->push_back($3); + $$= $1; + } + ; 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) + remember_name expr remember_end select_alias { - $2->is_autogenerated_name= FALSE; - $2->set_name($4.str, $4.length, system_charset_info); + /* + 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; + $2->set_name($4.str, $4.length, system_charset_info); + } + else + $2->set_name($1, (uint) ($3 - $1), YYTHD->charset()); + $$= $2; } - else - $2->set_name($1, (uint) ($3 - $1), YYTHD->charset()); - $$= $2; - } - ; + ; sum_expr: - AVG_SYM '(' in_sum_expr ')' - { $$=new Item_sum_avg($3); } - | AVG_SYM '(' DISTINCT in_sum_expr ')' - { $$=new Item_sum_avg_distinct($4); } - | BIT_AND '(' in_sum_expr ')' - { $$=new Item_sum_and($3); } - | BIT_OR '(' in_sum_expr ')' - { $$=new Item_sum_or($3); } - | BIT_XOR '(' in_sum_expr ')' - { $$=new Item_sum_xor($3); } - | COUNT_SYM '(' opt_all '*' ')' - { $$=new Item_sum_count(new Item_int((int32) 0L,1)); } - | COUNT_SYM '(' in_sum_expr ')' - { $$=new Item_sum_count($3); } - | COUNT_SYM '(' DISTINCT - { Select->in_sum_expr++; } - expr_list - { Select->in_sum_expr--; } - ')' - { $$=new Item_sum_count_distinct(* $5); } - | MIN_SYM '(' in_sum_expr ')' - { $$=new Item_sum_min($3); } -/* - According to ANSI SQL, DISTINCT is allowed and has - 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 ')' - { $$=new Item_sum_min($4); } - | MAX_SYM '(' in_sum_expr ')' - { $$=new Item_sum_max($3); } - | MAX_SYM '(' DISTINCT in_sum_expr ')' - { $$=new Item_sum_max($4); } - | STD_SYM '(' in_sum_expr ')' - { $$=new Item_sum_std($3, 0); } - | VARIANCE_SYM '(' in_sum_expr ')' - { $$=new Item_sum_variance($3, 0); } - | STDDEV_SAMP_SYM '(' in_sum_expr ')' - { $$=new Item_sum_std($3, 1); } - | VAR_SAMP_SYM '(' in_sum_expr ')' - { $$=new Item_sum_variance($3, 1); } - | SUM_SYM '(' in_sum_expr ')' - { $$=new Item_sum_sum($3); } - | SUM_SYM '(' DISTINCT in_sum_expr ')' - { $$=new Item_sum_sum_distinct($4); } - | GROUP_CONCAT_SYM '(' opt_distinct - { Select->in_sum_expr++; } - expr_list opt_gorder_clause - opt_gconcat_separator - ')' - { + AVG_SYM '(' in_sum_expr ')' + { $$=new Item_sum_avg($3); } + | AVG_SYM '(' DISTINCT in_sum_expr ')' + { $$=new Item_sum_avg_distinct($4); } + | BIT_AND '(' in_sum_expr ')' + { $$=new Item_sum_and($3); } + | BIT_OR '(' in_sum_expr ')' + { $$=new Item_sum_or($3); } + | BIT_XOR '(' in_sum_expr ')' + { $$=new Item_sum_xor($3); } + | COUNT_SYM '(' opt_all '*' ')' + { $$=new Item_sum_count(new Item_int((int32) 0L,1)); } + | COUNT_SYM '(' in_sum_expr ')' + { $$=new Item_sum_count($3); } + | COUNT_SYM '(' DISTINCT + { Select->in_sum_expr++; } + expr_list + { Select->in_sum_expr--; } + ')' + { $$=new Item_sum_count_distinct(* $5); } + | MIN_SYM '(' in_sum_expr ')' + { $$=new Item_sum_min($3); } + /* + According to ANSI SQL, DISTINCT is allowed and has + 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 ')' + { $$=new Item_sum_min($4); } + | MAX_SYM '(' in_sum_expr ')' + { $$=new Item_sum_max($3); } + | MAX_SYM '(' DISTINCT in_sum_expr ')' + { $$=new Item_sum_max($4); } + | STD_SYM '(' in_sum_expr ')' + { $$=new Item_sum_std($3, 0); } + | VARIANCE_SYM '(' in_sum_expr ')' + { $$=new Item_sum_variance($3, 0); } + | STDDEV_SAMP_SYM '(' in_sum_expr ')' + { $$=new Item_sum_std($3, 1); } + | VAR_SAMP_SYM '(' in_sum_expr ')' + { $$=new Item_sum_variance($3, 1); } + | SUM_SYM '(' in_sum_expr ')' + { $$=new Item_sum_sum($3); } + | SUM_SYM '(' DISTINCT in_sum_expr ')' + { $$=new Item_sum_sum_distinct($4); } + | GROUP_CONCAT_SYM '(' opt_distinct + { Select->in_sum_expr++; } + expr_list opt_gorder_clause + opt_gconcat_separator + ')' + { SELECT_LEX *sel= Select; - sel->in_sum_expr--; - $$=new Item_func_group_concat(Lex->current_context(), $3, $5, + sel->in_sum_expr--; + $$=new Item_func_group_concat(Lex->current_context(), $3, $5, sel->gorder_list, $7); - $5->empty(); - }; + $5->empty(); + } + ; variable: '@' @@ -7126,7 +7474,7 @@ variable: { $$= $3; } - ; + ; variable_aux: ident_or_text SET_VAR expr @@ -7154,96 +7502,120 @@ variable_aux: ; opt_distinct: - /* empty */ { $$ = 0; } - |DISTINCT { $$ = 1; }; + /* empty */ { $$ = 0; } + | DISTINCT { $$ = 1; } + ; opt_gconcat_separator: - /* empty */ - { - $$= new (YYTHD->mem_root) String(",", 1, &my_charset_latin1); - } - | SEPARATOR_SYM text_string { $$ = $2; }; - + /* empty */ + { + $$= new (YYTHD->mem_root) String(",", 1, &my_charset_latin1); + } + | SEPARATOR_SYM text_string { $$ = $2; } + ; opt_gorder_clause: - /* empty */ - { + /* empty */ + { Select->gorder_list = NULL; - } - | order_clause + } + | order_clause { SELECT_LEX *select= Select; select->gorder_list= - (SQL_LIST*) sql_memdup((char*) &select->order_list, - sizeof(st_sql_list)); - select->order_list.empty(); - }; - + (SQL_LIST*) sql_memdup((char*) &select->order_list, + sizeof(st_sql_list)); + select->order_list.empty(); + } + ; in_sum_expr: - opt_all - { - LEX *lex= Lex; - if (lex->current_select->inc_in_sum_expr()) - { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; - } - } - expr - { - Select->in_sum_expr--; - $$= $3; - }; + opt_all + { + LEX *lex= Lex; + if (lex->current_select->inc_in_sum_expr()) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } + } + expr + { + Select->in_sum_expr--; + $$= $3; + } + ; cast_type: - BINARY opt_len { $$=ITEM_CAST_CHAR; Lex->charset= &my_charset_bin; Lex->dec= 0; } - | CHAR_SYM opt_len opt_binary { $$=ITEM_CAST_CHAR; Lex->dec= 0; } - | NCHAR_SYM opt_len { $$=ITEM_CAST_CHAR; Lex->charset= national_charset_info; Lex->dec=0; } - | SIGNED_SYM { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } - | SIGNED_SYM INT_SYM { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } - | UNSIGNED { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } - | UNSIGNED INT_SYM { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } - | DATE_SYM { $$=ITEM_CAST_DATE; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } - | TIME_SYM { $$=ITEM_CAST_TIME; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } - | DATETIME { $$=ITEM_CAST_DATETIME; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } - | DECIMAL_SYM float_options { $$=ITEM_CAST_DECIMAL; Lex->charset= NULL; } - ; + BINARY opt_len + { $$=ITEM_CAST_CHAR; Lex->charset= &my_charset_bin; Lex->dec= 0; } + | CHAR_SYM opt_len opt_binary + { $$=ITEM_CAST_CHAR; Lex->dec= 0; } + | NCHAR_SYM opt_len + { $$=ITEM_CAST_CHAR; Lex->charset= national_charset_info; Lex->dec=0; } + | SIGNED_SYM + { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | SIGNED_SYM INT_SYM + { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | UNSIGNED + { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | UNSIGNED INT_SYM + { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | DATE_SYM + { $$=ITEM_CAST_DATE; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | TIME_SYM + { $$=ITEM_CAST_TIME; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | DATETIME + { $$=ITEM_CAST_DATETIME; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | DECIMAL_SYM float_options + { $$=ITEM_CAST_DECIMAL; Lex->charset= NULL; } + ; opt_expr_list: - /* empty */ { $$= NULL; } - | expr_list { $$= $1;} - ; + /* empty */ { $$= NULL; } + | expr_list { $$= $1;} + ; expr_list: - { Select->expr_list.push_front(new List<Item>); } - expr_list2 - { $$= Select->expr_list.pop(); }; - -expr_list2: - expr { Select->expr_list.head()->push_back($1); } - | expr_list2 ',' expr { Select->expr_list.head()->push_back($3); }; + expr + { + $$= new (YYTHD->mem_root) List<Item>; + $$->push_back($1); + } + | expr_list ',' expr + { + $1->push_back($3); + $$= $1; + } + ; ident_list_arg: ident_list { $$= $1; } - | '(' ident_list ')' { $$= $2; }; + | '(' ident_list ')' { $$= $2; } + ; ident_list: - { Select->expr_list.push_front(new List<Item>); } - ident_list2 - { $$= Select->expr_list.pop(); }; - -ident_list2: - simple_ident { Select->expr_list.head()->push_back($1); } - | ident_list2 ',' simple_ident { Select->expr_list.head()->push_back($3); }; + simple_ident + { + $$= new (YYTHD->mem_root) List<Item>; + $$->push_back($1); + } + | ident_list ',' simple_ident + { + $1->push_back($3); + $$= $1; + } + ; opt_expr: - /* empty */ { $$= NULL; } - | expr { $$= $1; }; + /* empty */ { $$= NULL; } + | expr { $$= $1; } + ; opt_else: - /* empty */ { $$= NULL; } - | ELSE expr { $$= $2; }; + /* empty */ { $$= NULL; } + | ELSE expr { $$= $2; } + ; when_list: WHEN_SYM expr THEN_SYM expr @@ -7262,22 +7634,22 @@ when_list: /* Warning - may return NULL in case of incomplete SELECT */ table_ref: - table_factor { $$=$1; } + table_factor { $$=$1; } | join_table { - LEX *lex= Lex; + LEX *lex= Lex; if (!($$= lex->current_select->nest_last_join(lex->thd))) MYSQL_YYABORT; } ; join_table_list: - derived_table_list { MYSQL_YYABORT_UNLESS($$=$1); } - ; + derived_table_list { MYSQL_YYABORT_UNLESS($$=$1); } + ; /* Warning - may return NULL in case of incomplete SELECT */ derived_table_list: - table_ref { $$=$1; } + table_ref { $$=$1; } | derived_table_list ',' table_ref { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); @@ -7292,17 +7664,17 @@ derived_table_list: and subsequent optimization phases. */ join_table: -/* INNER JOIN variants */ - /* - Use %prec to evaluate production 'table_ref' before 'normal_join' - so that [INNER | CROSS] JOIN is properly nested as other - left-associative joins. - */ - table_ref %prec TABLE_REF_PRIORITY normal_join table_ref + /* INNER JOIN variants */ + /* + Use %prec to evaluate production 'table_ref' before 'normal_join' + so that [INNER | CROSS] JOIN is properly nested as other + left-associative joins. + */ + table_ref normal_join table_ref %prec TABLE_REF_PRIORITY { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); } - | table_ref STRAIGHT_JOIN table_factor - { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); $3->straight=1; } - | table_ref normal_join table_ref + | table_ref STRAIGHT_JOIN table_factor + { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); $3->straight=1; } + | table_ref normal_join table_ref ON { MYSQL_YYABORT_UNLESS($1 && $3); @@ -7312,7 +7684,7 @@ join_table: Select->parsing_place= IN_ON; } expr - { + { add_join_on($3,$6); Lex->pop_context(); Select->parsing_place= NO_MATTER; @@ -7333,21 +7705,21 @@ join_table: Lex->pop_context(); Select->parsing_place= NO_MATTER; } - | table_ref normal_join table_ref - USING - { + | table_ref normal_join table_ref + USING + { MYSQL_YYABORT_UNLESS($1 && $3); - } - '(' using_list ')' + } + '(' using_list ')' { add_join_natural($1,$3,$7,Select); $$=$3; } - | table_ref NATURAL JOIN_SYM table_factor - { + | table_ref NATURAL JOIN_SYM table_factor + { MYSQL_YYABORT_UNLESS($1 && ($$=$4)); add_join_natural($1,$4,NULL,Select); } -/* LEFT JOIN variants */ - | table_ref LEFT opt_outer JOIN_SYM table_ref + /* LEFT JOIN variants */ + | table_ref LEFT opt_outer JOIN_SYM table_ref ON { MYSQL_YYABORT_UNLESS($1 && $5); @@ -7357,33 +7729,33 @@ join_table: Select->parsing_place= IN_ON; } expr - { + { add_join_on($5,$8); Lex->pop_context(); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; Select->parsing_place= NO_MATTER; } - | table_ref LEFT opt_outer JOIN_SYM table_factor - { + | table_ref LEFT opt_outer JOIN_SYM table_factor + { MYSQL_YYABORT_UNLESS($1 && $5); - } - USING '(' using_list ')' + } + USING '(' using_list ')' { add_join_natural($1,$5,$9,Select); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; } - | table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor - { + | table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor + { MYSQL_YYABORT_UNLESS($1 && $6); - add_join_natural($1,$6,NULL,Select); - $6->outer_join|=JOIN_TYPE_LEFT; - $$=$6; - } + add_join_natural($1,$6,NULL,Select); + $6->outer_join|=JOIN_TYPE_LEFT; + $$=$6; + } -/* RIGHT JOIN variants */ - | table_ref RIGHT opt_outer JOIN_SYM table_ref + /* RIGHT JOIN variants */ + | table_ref RIGHT opt_outer JOIN_SYM table_ref ON { MYSQL_YYABORT_UNLESS($1 && $5); @@ -7394,55 +7766,56 @@ join_table: } expr { - LEX *lex= Lex; + LEX *lex= Lex; if (!($$= lex->current_select->convert_right_join())) MYSQL_YYABORT; add_join_on($$, $8); Lex->pop_context(); Select->parsing_place= NO_MATTER; } - | table_ref RIGHT opt_outer JOIN_SYM table_factor - { + | table_ref RIGHT opt_outer JOIN_SYM table_factor + { MYSQL_YYABORT_UNLESS($1 && $5); - } - USING '(' using_list ')' + } + USING '(' using_list ')' { - LEX *lex= Lex; + LEX *lex= Lex; if (!($$= lex->current_select->convert_right_join())) MYSQL_YYABORT; add_join_natural($$,$5,$9,Select); } - | table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor - { + | table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor + { MYSQL_YYABORT_UNLESS($1 && $6); - add_join_natural($6,$1,NULL,Select); - LEX *lex= Lex; + add_join_natural($6,$1,NULL,Select); + LEX *lex= Lex; if (!($$= lex->current_select->convert_right_join())) MYSQL_YYABORT; - }; + } + ; normal_join: - JOIN_SYM {} - | INNER_SYM JOIN_SYM {} - | CROSS JOIN_SYM {} - ; + JOIN_SYM {} + | INNER_SYM JOIN_SYM {} + | CROSS JOIN_SYM {} + ; /* Warning - may return NULL in case of incomplete SELECT */ table_factor: - { - SELECT_LEX *sel= Select; - sel->table_join_options= 0; - } - table_ident opt_table_alias opt_key_definition - { - if (!($$= Select->add_table_to_list(YYTHD, $2, $3, - Select->get_table_join_options(), - Lex->lock_option, - Select->pop_index_hints()))) - MYSQL_YYABORT; - Select->add_joined_table($$); - } - | '{' ident table_ref LEFT OUTER JOIN_SYM table_ref + { + SELECT_LEX *sel= Select; + sel->table_join_options= 0; + } + table_ident opt_table_alias opt_key_definition + { + if (!($$= Select->add_table_to_list(YYTHD, $2, $3, + Select->get_table_join_options(), + Lex->lock_option, + Select->pop_index_hints()))) + MYSQL_YYABORT; + Select->add_joined_table($$); + } + | '{' ident table_ref LEFT OUTER JOIN_SYM table_ref ON { /* Change the current name resolution context to a local context. */ @@ -7451,8 +7824,8 @@ table_factor: } expr '}' - { - LEX *lex= Lex; + { + LEX *lex= Lex; MYSQL_YYABORT_UNLESS($3 && $7); add_join_on($7,$10); Lex->pop_context(); @@ -7461,19 +7834,19 @@ table_factor: if (!($$= lex->current_select->nest_last_join(lex->thd))) MYSQL_YYABORT; } - | select_derived_init get_select_lex select_derived2 + | select_derived_init get_select_lex select_derived2 { LEX *lex= Lex; SELECT_LEX *sel= lex->current_select; if ($1) { - if (sel->set_braces(1)) - { + if (sel->set_braces(1)) + { my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; - } + MYSQL_YYABORT; + } /* select in braces, can't contain global parameters */ - if (sel->master_unit()->fake_select_lex) + if (sel->master_unit()->fake_select_lex) sel->master_unit()->global_parameters= sel->master_unit()->fake_select_lex; } @@ -7483,54 +7856,52 @@ table_factor: /* incomplete derived tables return NULL, we must be nested in select_derived rule to be here. */ } - | '(' get_select_lex select_derived union_opt ')' opt_table_alias - { - /* Use $2 instead of Lex->current_select as derived table will - alter value of Lex->current_select. */ - - if (!($3 || $6) && $2->embedding && - !$2->embedding->nested_join->join_list.elements) - { - /* we have a derived table ($3 == NULL) but no alias, - Since we are nested in further parentheses so we - can pass NULL to the outer level parentheses - Permits parsing of "((((select ...))) as xyz)" */ - $$= 0; - } - else - if (!$3) + | '(' get_select_lex select_derived union_opt ')' opt_table_alias { - /* Handle case of derived table, alias may be NULL if there - are no outer parentheses, add_table_to_list() will throw - error in this case */ - LEX *lex=Lex; - SELECT_LEX *sel= lex->current_select; - SELECT_LEX_UNIT *unit= sel->master_unit(); - lex->current_select= sel= unit->outer_select(); - if (!($$= sel-> - add_table_to_list(lex->thd, new Table_ident(unit), $6, 0, - TL_READ))) - - MYSQL_YYABORT; - sel->add_joined_table($$); - lex->pop_context(); + /* Use $2 instead of Lex->current_select as derived table will + alter value of Lex->current_select. */ + + if (!($3 || $6) && $2->embedding && + !$2->embedding->nested_join->join_list.elements) + { + /* we have a derived table ($3 == NULL) but no alias, + Since we are nested in further parentheses so we + can pass NULL to the outer level parentheses + Permits parsing of "((((select ...))) as xyz)" */ + $$= 0; + } + else if (!$3) + { + /* Handle case of derived table, alias may be NULL if there + are no outer parentheses, add_table_to_list() will throw + error in this case */ + LEX *lex=Lex; + SELECT_LEX *sel= lex->current_select; + SELECT_LEX_UNIT *unit= sel->master_unit(); + lex->current_select= sel= unit->outer_select(); + if (!($$= sel->add_table_to_list(lex->thd, + new Table_ident(unit), $6, 0, + TL_READ))) + + MYSQL_YYABORT; + sel->add_joined_table($$); + lex->pop_context(); + } + else if ($4 || $6) + { + /* simple nested joins cannot have aliases or unions */ + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } + else + $$= $3; } - else - if ($4 || $6) - { - /* simple nested joins cannot have aliases or unions */ - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; - } - else - $$= $3; - } ; /* handle contents of parentheses in join expression */ select_derived: - get_select_lex - { + get_select_lex + { LEX *lex= Lex; if ($1->init_nested_join(lex->thd)) MYSQL_YYABORT; @@ -7546,37 +7917,37 @@ select_derived: if (!$3 && $$) { my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; + MYSQL_YYABORT; } } - ; + ; select_derived2: - { - LEX *lex= Lex; - lex->derived_tables|= DERIVED_SUBQUERY; - if (!lex->expr_allows_subselect || - lex->sql_command == (int)SQLCOM_PURGE) - { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; - } - if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE || - mysql_new_select(lex, 1)) - MYSQL_YYABORT; - mysql_init_select(lex); - lex->current_select->linkage= DERIVED_TABLE_TYPE; - lex->current_select->parsing_place= SELECT_LIST; - } - select_options select_item_list - { - Select->parsing_place= NO_MATTER; - } - opt_select_from + { + LEX *lex= Lex; + lex->derived_tables|= DERIVED_SUBQUERY; + if (!lex->expr_allows_subselect || + lex->sql_command == (int)SQLCOM_PURGE) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } + if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE || + mysql_new_select(lex, 1)) + MYSQL_YYABORT; + mysql_init_select(lex); + lex->current_select->linkage= DERIVED_TABLE_TYPE; + lex->current_select->parsing_place= SELECT_LIST; + } + select_options select_item_list + { + Select->parsing_place= NO_MATTER; + } + opt_select_from ; get_select_lex: - /* Empty */ { $$= Select; } + /* Empty */ { $$= Select; } ; select_derived_init: @@ -7593,11 +7964,11 @@ select_derived_init: SELECT_LEX *sel= lex->current_select; TABLE_LIST *embedding; if (!sel->embedding || sel->end_nested_join(lex->thd)) - { + { /* we are not in parentheses */ my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; - } + MYSQL_YYABORT; + } embedding= Select->embedding; $$= embedding && !embedding->nested_join->join_list.elements; @@ -7606,226 +7977,231 @@ select_derived_init: ; opt_outer: - /* empty */ {} - | OUTER {}; + /* empty */ {} + | OUTER {} + ; index_hint_clause: - /* empty */ + /* empty */ { $$= global_system_variables.old_mode ? INDEX_HINT_MASK_JOIN : INDEX_HINT_MASK_ALL; - } - | FOR_SYM JOIN_SYM { $$= INDEX_HINT_MASK_JOIN; } - | FOR_SYM ORDER_SYM BY { $$= INDEX_HINT_MASK_ORDER; } - | FOR_SYM GROUP_SYM BY { $$= INDEX_HINT_MASK_GROUP; } - ; + } + | FOR_SYM JOIN_SYM { $$= INDEX_HINT_MASK_JOIN; } + | FOR_SYM ORDER_SYM BY { $$= INDEX_HINT_MASK_ORDER; } + | FOR_SYM GROUP_SYM BY { $$= INDEX_HINT_MASK_GROUP; } + ; index_hint_type: - FORCE_SYM { $$= INDEX_HINT_FORCE; } - | IGNORE_SYM { $$= INDEX_HINT_IGNORE; } - ; + FORCE_SYM { $$= INDEX_HINT_FORCE; } + | IGNORE_SYM { $$= INDEX_HINT_IGNORE; } + ; index_hint_definition: - index_hint_type key_or_index index_hint_clause - { - Select->set_index_hint_type($1, $3); - } - '(' key_usage_list ')' - | USE_SYM key_or_index index_hint_clause - { - Select->set_index_hint_type(INDEX_HINT_USE, $3); - } - '(' opt_key_usage_list ')' + index_hint_type key_or_index index_hint_clause + { + Select->set_index_hint_type($1, $3); + } + '(' key_usage_list ')' + | USE_SYM key_or_index index_hint_clause + { + Select->set_index_hint_type(INDEX_HINT_USE, $3); + } + '(' opt_key_usage_list ')' ; index_hints_list: - index_hint_definition - | index_hints_list index_hint_definition - ; + index_hint_definition + | index_hints_list index_hint_definition + ; opt_index_hints_list: - /* empty */ - | { Select->alloc_index_hints(YYTHD); } index_hints_list - ; + /* empty */ + | { Select->alloc_index_hints(YYTHD); } index_hints_list + ; opt_key_definition: - { Select->clear_index_hints(); } - opt_index_hints_list - ; + { Select->clear_index_hints(); } + opt_index_hints_list + ; opt_key_usage_list: - /* empty */ { Select->add_index_hint(YYTHD, NULL, 0); } - | key_usage_list {} - ; + /* empty */ { Select->add_index_hint(YYTHD, NULL, 0); } + | key_usage_list {} + ; key_usage_element: - ident { Select->add_index_hint(YYTHD, $1.str, $1.length); } - | PRIMARY_SYM - { - Select->add_index_hint(YYTHD, (char *)"PRIMARY", 7); - } + ident + { Select->add_index_hint(YYTHD, $1.str, $1.length); } + | PRIMARY_SYM + { Select->add_index_hint(YYTHD, (char *)"PRIMARY", 7); } ; key_usage_list: - key_usage_element - | key_usage_list ',' key_usage_element + key_usage_element + | key_usage_list ',' key_usage_element ; using_list: - ident - { + ident + { if (!($$= new List<String>)) - MYSQL_YYABORT; + MYSQL_YYABORT; $$->push_back(new (YYTHD->mem_root) String((const char *) $1.str, $1.length, system_charset_info)); - } - | using_list ',' ident - { + } + | using_list ',' ident + { $1->push_back(new (YYTHD->mem_root) String((const char *) $3.str, $3.length, system_charset_info)); $$= $1; - }; + } + ; interval: - interval_time_st {} - | DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; } - | DAY_MICROSECOND_SYM { $$=INTERVAL_DAY_MICROSECOND; } - | DAY_MINUTE_SYM { $$=INTERVAL_DAY_MINUTE; } - | DAY_SECOND_SYM { $$=INTERVAL_DAY_SECOND; } - | HOUR_MICROSECOND_SYM { $$=INTERVAL_HOUR_MICROSECOND; } - | HOUR_MINUTE_SYM { $$=INTERVAL_HOUR_MINUTE; } - | HOUR_SECOND_SYM { $$=INTERVAL_HOUR_SECOND; } - | MICROSECOND_SYM { $$=INTERVAL_MICROSECOND; } - | MINUTE_MICROSECOND_SYM { $$=INTERVAL_MINUTE_MICROSECOND; } - | MINUTE_SECOND_SYM { $$=INTERVAL_MINUTE_SECOND; } - | SECOND_MICROSECOND_SYM { $$=INTERVAL_SECOND_MICROSECOND; } - | YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; }; + interval_time_st {} + | DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; } + | DAY_MICROSECOND_SYM { $$=INTERVAL_DAY_MICROSECOND; } + | DAY_MINUTE_SYM { $$=INTERVAL_DAY_MINUTE; } + | DAY_SECOND_SYM { $$=INTERVAL_DAY_SECOND; } + | HOUR_MICROSECOND_SYM { $$=INTERVAL_HOUR_MICROSECOND; } + | HOUR_MINUTE_SYM { $$=INTERVAL_HOUR_MINUTE; } + | HOUR_SECOND_SYM { $$=INTERVAL_HOUR_SECOND; } + | MICROSECOND_SYM { $$=INTERVAL_MICROSECOND; } + | MINUTE_MICROSECOND_SYM { $$=INTERVAL_MINUTE_MICROSECOND; } + | MINUTE_SECOND_SYM { $$=INTERVAL_MINUTE_SECOND; } + | SECOND_MICROSECOND_SYM { $$=INTERVAL_SECOND_MICROSECOND; } + | YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; } + ; interval_time_st: - DAY_SYM { $$=INTERVAL_DAY; } - | WEEK_SYM { $$=INTERVAL_WEEK; } - | HOUR_SYM { $$=INTERVAL_HOUR; } - | FRAC_SECOND_SYM { $$=INTERVAL_MICROSECOND; } - | MINUTE_SYM { $$=INTERVAL_MINUTE; } - | MONTH_SYM { $$=INTERVAL_MONTH; } - | QUARTER_SYM { $$=INTERVAL_QUARTER; } - | SECOND_SYM { $$=INTERVAL_SECOND; } - | YEAR_SYM { $$=INTERVAL_YEAR; } + DAY_SYM { $$=INTERVAL_DAY; } + | WEEK_SYM { $$=INTERVAL_WEEK; } + | HOUR_SYM { $$=INTERVAL_HOUR; } + | FRAC_SECOND_SYM { $$=INTERVAL_MICROSECOND; } + | MINUTE_SYM { $$=INTERVAL_MINUTE; } + | MONTH_SYM { $$=INTERVAL_MONTH; } + | QUARTER_SYM { $$=INTERVAL_QUARTER; } + | SECOND_SYM { $$=INTERVAL_SECOND; } + | YEAR_SYM { $$=INTERVAL_YEAR; } ; date_time_type: - DATE_SYM {$$=MYSQL_TIMESTAMP_DATE;} - | TIME_SYM {$$=MYSQL_TIMESTAMP_TIME;} - | DATETIME {$$=MYSQL_TIMESTAMP_DATETIME;} - | TIMESTAMP {$$=MYSQL_TIMESTAMP_DATETIME;} + DATE_SYM {$$=MYSQL_TIMESTAMP_DATE;} + | TIME_SYM {$$=MYSQL_TIMESTAMP_TIME;} + | DATETIME {$$=MYSQL_TIMESTAMP_DATETIME;} + | TIMESTAMP {$$=MYSQL_TIMESTAMP_DATETIME;} ; table_alias: - /* empty */ - | AS - | EQ; + /* empty */ + | AS + | EQ + ; opt_table_alias: - /* empty */ { $$=0; } - | table_alias ident - { $$= (LEX_STRING*) sql_memdup(&$2,sizeof(LEX_STRING)); }; + /* empty */ { $$=0; } + | table_alias ident + { $$= (LEX_STRING*) sql_memdup(&$2,sizeof(LEX_STRING)); } + ; opt_all: - /* empty */ - | ALL - ; + /* empty */ + | ALL + ; where_clause: - /* empty */ { Select->where= 0; } - | WHERE + /* empty */ { Select->where= 0; } + | WHERE { Select->parsing_place= IN_WHERE; } expr - { + { SELECT_LEX *select= Select; - select->where= $3; + select->where= $3; select->parsing_place= NO_MATTER; - if ($3) - $3->top_level_item(); - } - ; + if ($3) + $3->top_level_item(); + } + ; having_clause: - /* empty */ - | HAVING - { - Select->parsing_place= IN_HAVING; - } - expr - { - SELECT_LEX *sel= Select; - sel->having= $3; - sel->parsing_place= NO_MATTER; - if ($3) - $3->top_level_item(); - } - ; + /* empty */ + | HAVING + { + Select->parsing_place= IN_HAVING; + } + expr + { + SELECT_LEX *sel= Select; + sel->having= $3; + sel->parsing_place= NO_MATTER; + if ($3) + $3->top_level_item(); + } + ; opt_escape: - ESCAPE_SYM simple_expr + ESCAPE_SYM simple_expr { Lex->escape_used= TRUE; $$= $2; } - | /* empty */ + | /* empty */ { Lex->escape_used= FALSE; $$= ((YYTHD->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) ? - new Item_string("", 0, &my_charset_latin1) : + new Item_string("", 0, &my_charset_latin1) : new Item_string("\\", 1, &my_charset_latin1)); } ; - /* group by statement in select */ group_clause: - /* empty */ - | GROUP_SYM BY group_list olap_opt; + /* empty */ + | GROUP_SYM BY group_list olap_opt + ; group_list: - group_list ',' order_ident order_dir - { if (add_group_to_list(YYTHD, $3,(bool) $4)) MYSQL_YYABORT; } - | order_ident order_dir - { if (add_group_to_list(YYTHD, $1,(bool) $2)) MYSQL_YYABORT; }; + group_list ',' order_ident order_dir + { if (add_group_to_list(YYTHD, $3,(bool) $4)) MYSQL_YYABORT; } + | order_ident order_dir + { if (add_group_to_list(YYTHD, $1,(bool) $2)) MYSQL_YYABORT; } + ; olap_opt: - /* empty */ {} - | WITH CUBE_SYM - { - LEX *lex=Lex; - if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) - { - my_error(ER_WRONG_USAGE, MYF(0), "WITH CUBE", - "global union parameters"); - MYSQL_YYABORT; - } - lex->current_select->olap= CUBE_TYPE; - my_error(ER_NOT_SUPPORTED_YET, MYF(0), "CUBE"); - MYSQL_YYABORT; /* To be deleted in 5.1 */ - } - | WITH ROLLUP_SYM - { - LEX *lex= Lex; - if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) - { - my_error(ER_WRONG_USAGE, MYF(0), "WITH ROLLUP", - "global union parameters"); - MYSQL_YYABORT; - } - lex->current_select->olap= ROLLUP_TYPE; - } - ; + /* empty */ {} + | WITH CUBE_SYM + { + LEX *lex=Lex; + if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) + { + my_error(ER_WRONG_USAGE, MYF(0), "WITH CUBE", + "global union parameters"); + MYSQL_YYABORT; + } + lex->current_select->olap= CUBE_TYPE; + my_error(ER_NOT_SUPPORTED_YET, MYF(0), "CUBE"); + MYSQL_YYABORT; /* To be deleted in 5.1 */ + } + | WITH ROLLUP_SYM + { + LEX *lex= Lex; + if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) + { + my_error(ER_WRONG_USAGE, MYF(0), "WITH ROLLUP", + "global union parameters"); + MYSQL_YYABORT; + } + lex->current_select->olap= ROLLUP_TYPE; + } + ; /* Order by statement in ALTER TABLE @@ -7855,159 +8231,165 @@ alter_order_item: */ opt_order_clause: - /* empty */ - | order_clause; + /* empty */ + | order_clause + ; order_clause: - ORDER_SYM BY - { - LEX *lex=Lex; - SELECT_LEX *sel= lex->current_select; - SELECT_LEX_UNIT *unit= sel-> master_unit(); - if (sel->linkage != GLOBAL_OPTIONS_TYPE && - sel->olap != UNSPECIFIED_OLAP_TYPE) - { - my_error(ER_WRONG_USAGE, MYF(0), - "CUBE/ROLLUP", "ORDER BY"); - MYSQL_YYABORT; - } - if (lex->sql_command != SQLCOM_ALTER_TABLE && !unit->fake_select_lex) + ORDER_SYM BY { - /* - A query of the of the form (SELECT ...) ORDER BY order_list is - executed in the same way as the query - SELECT ... ORDER BY order_list - unless the SELECT construct contains ORDER BY or LIMIT clauses. - Otherwise we create a fake SELECT_LEX if it has not been created - yet. - */ - SELECT_LEX *first_sl= unit->first_select(); - if (!unit->is_union() && - (first_sl->order_list.elements || - first_sl->select_limit) && - unit->add_fake_select_lex(lex->thd)) + LEX *lex=Lex; + SELECT_LEX *sel= lex->current_select; + SELECT_LEX_UNIT *unit= sel-> master_unit(); + if (sel->linkage != GLOBAL_OPTIONS_TYPE && + sel->olap != UNSPECIFIED_OLAP_TYPE) + { + my_error(ER_WRONG_USAGE, MYF(0), + "CUBE/ROLLUP", "ORDER BY"); MYSQL_YYABORT; + } + if (lex->sql_command != SQLCOM_ALTER_TABLE && !unit->fake_select_lex) + { + /* + A query of the of the form (SELECT ...) ORDER BY order_list is + executed in the same way as the query + SELECT ... ORDER BY order_list + unless the SELECT construct contains ORDER BY or LIMIT clauses. + Otherwise we create a fake SELECT_LEX if it has not been created + yet. + */ + SELECT_LEX *first_sl= unit->first_select(); + if (!unit->is_union() && + (first_sl->order_list.elements || + first_sl->select_limit) && + unit->add_fake_select_lex(lex->thd)) + MYSQL_YYABORT; + } } - } order_list; + order_list + ; order_list: - order_list ',' order_ident order_dir - { if (add_order_to_list(YYTHD, $3,(bool) $4)) MYSQL_YYABORT; } - | order_ident order_dir - { if (add_order_to_list(YYTHD, $1,(bool) $2)) MYSQL_YYABORT; }; + order_list ',' order_ident order_dir + { if (add_order_to_list(YYTHD, $3,(bool) $4)) MYSQL_YYABORT; } + | order_ident order_dir + { if (add_order_to_list(YYTHD, $1,(bool) $2)) MYSQL_YYABORT; } + ; order_dir: - /* empty */ { $$ = 1; } - | ASC { $$ =1; } - | DESC { $$ =0; }; - + /* empty */ { $$ = 1; } + | ASC { $$ =1; } + | DESC { $$ =0; } + ; opt_limit_clause_init: - /* empty */ - { - LEX *lex= Lex; - SELECT_LEX *sel= lex->current_select; - sel->offset_limit= 0; - sel->select_limit= 0; - } - | limit_clause {} - ; + /* empty */ + { + LEX *lex= Lex; + SELECT_LEX *sel= lex->current_select; + sel->offset_limit= 0; + sel->select_limit= 0; + } + | limit_clause {} + ; opt_limit_clause: - /* empty */ {} - | limit_clause {} - ; + /* empty */ {} + | limit_clause {} + ; limit_clause: - LIMIT limit_options {} - ; + LIMIT limit_options {} + ; limit_options: - limit_option - { + limit_option + { SELECT_LEX *sel= Select; sel->select_limit= $1; sel->offset_limit= 0; - sel->explicit_limit= 1; - } - | limit_option ',' limit_option - { - SELECT_LEX *sel= Select; - sel->select_limit= $3; - sel->offset_limit= $1; - sel->explicit_limit= 1; - } - | limit_option OFFSET_SYM limit_option - { - SELECT_LEX *sel= Select; - sel->select_limit= $1; - sel->offset_limit= $3; - sel->explicit_limit= 1; - } - ; + sel->explicit_limit= 1; + } + | limit_option ',' limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $3; + sel->offset_limit= $1; + sel->explicit_limit= 1; + } + | limit_option OFFSET_SYM limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= $3; + sel->explicit_limit= 1; + } + ; + limit_option: - param_marker - { - ((Item_param *) $1)->set_strict_type(INT_RESULT); - } + param_marker + { + ((Item_param *) $1)->set_strict_type(INT_RESULT); + } | ULONGLONG_NUM { $$= new Item_uint($1.str, $1.length); } - | LONG_NUM { $$= new Item_uint($1.str, $1.length); } + | LONG_NUM { $$= new Item_uint($1.str, $1.length); } | NUM { $$= new Item_uint($1.str, $1.length); } ; delete_limit_clause: - /* empty */ - { - LEX *lex=Lex; - lex->current_select->select_limit= 0; - } - | LIMIT limit_option - { - SELECT_LEX *sel= Select; - sel->select_limit= $2; - sel->explicit_limit= 1; - }; + /* empty */ + { + LEX *lex=Lex; + lex->current_select->select_limit= 0; + } + | LIMIT limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $2; + sel->explicit_limit= 1; + } + ; ulong_num: NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } - | 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); } + | 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); } | DECIMAL_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } - | FLOAT_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | FLOAT_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } ; real_ulong_num: NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } - | 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); } + | 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 { MYSQL_YYABORT; } ; 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); } - | DECIMAL_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } - | FLOAT_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } - ; + 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); } + | DECIMAL_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | FLOAT_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + ; 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); } + 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 { MYSQL_YYABORT; } ; dec_num_error: - dec_num - { my_parse_error(ER(ER_ONLY_INTEGERS_ALLOWED)); } + dec_num + { my_parse_error(ER(ER_ONLY_INTEGERS_ALLOWED)); } ; dec_num: - DECIMAL_NUM - | FLOAT_NUM + DECIMAL_NUM + | FLOAT_NUM ; choice: @@ -8016,10 +8398,10 @@ choice: ; procedure_clause: - /* empty */ - | PROCEDURE ident /* Procedure name */ - { - LEX *lex=Lex; + /* empty */ + | PROCEDURE ident /* Procedure name */ + { + LEX *lex=Lex; if (! lex->parsing_options.allows_select_procedure) { @@ -8027,225 +8409,227 @@ procedure_clause: MYSQL_YYABORT; } - if (&lex->select_lex != lex->current_select) - { - my_error(ER_WRONG_USAGE, MYF(0), "PROCEDURE", "subquery"); - MYSQL_YYABORT; - } - lex->proc_list.elements=0; - lex->proc_list.first=0; - lex->proc_list.next= (uchar**) &lex->proc_list.first; - if (add_proc_to_list(lex->thd, new Item_field(&lex-> + if (&lex->select_lex != lex->current_select) + { + my_error(ER_WRONG_USAGE, MYF(0), "PROCEDURE", "subquery"); + MYSQL_YYABORT; + } + lex->proc_list.elements=0; + lex->proc_list.first=0; + lex->proc_list.next= (uchar**) &lex->proc_list.first; + if (add_proc_to_list(lex->thd, new Item_field(&lex-> current_select-> context, NULL,NULL,$2.str))) - MYSQL_YYABORT; - Lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - } - '(' procedure_list ')'; - + MYSQL_YYABORT; + Lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + } + '(' procedure_list ')' + ; procedure_list: - /* empty */ {} - | procedure_list2 {}; + /* empty */ {} + | procedure_list2 {} + ; procedure_list2: - procedure_list2 ',' procedure_item - | procedure_item; + procedure_list2 ',' procedure_item + | procedure_item + ; procedure_item: - remember_name expr remember_end - { + remember_name expr remember_end + { THD *thd= YYTHD; - if (add_proc_to_list(thd, $2)) - MYSQL_YYABORT; - if (!$2->name) + if (add_proc_to_list(thd, $2)) + MYSQL_YYABORT; + if (!$2->name) $2->set_name($1, (uint) ($3 - $1), thd->charset()); - } - ; - + } + ; select_var_list_init: - { - LEX *lex=Lex; - if (!lex->describe && (!(lex->result= new select_dumpvar()))) - MYSQL_YYABORT; - } - select_var_list - {} - ; + { + LEX *lex=Lex; + if (!lex->describe && (!(lex->result= new select_dumpvar()))) + MYSQL_YYABORT; + } + select_var_list + {} + ; select_var_list: - select_var_list ',' select_var_ident - | select_var_ident {} - ; + select_var_list ',' select_var_ident + | select_var_ident {} + ; select_var_ident: - '@' ident_or_text - { - LEX *lex=Lex; - if (lex->result) - ((select_dumpvar *)lex->result)->var_list.push_back( new my_var($2,0,0,(enum_field_types)0)); - else - /* - The parser won't create select_result instance only - if it's an EXPLAIN. - */ - DBUG_ASSERT(lex->describe); - } - | ident_or_text - { - LEX *lex=Lex; - sp_variable_t *t; - - if (!lex->spcont || !(t=lex->spcont->find_variable(&$1))) - { - my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str); - MYSQL_YYABORT; - } - if (lex->result) - { - my_var *var; - ((select_dumpvar *)lex->result)-> - var_list.push_back(var= new my_var($1,1,t->offset,t->type)); + '@' ident_or_text + { + LEX *lex=Lex; + if (lex->result) + ((select_dumpvar *)lex->result)->var_list.push_back( new my_var($2,0,0,(enum_field_types)0)); + else + /* + The parser won't create select_result instance only + if it's an EXPLAIN. + */ + DBUG_ASSERT(lex->describe); + } + | ident_or_text + { + LEX *lex=Lex; + sp_variable_t *t; + + if (!lex->spcont || !(t=lex->spcont->find_variable(&$1))) + { + my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str); + MYSQL_YYABORT; + } + if (lex->result) + { + my_var *var; + ((select_dumpvar *)lex->result)-> + var_list.push_back(var= new my_var($1,1,t->offset,t->type)); #ifndef DBUG_OFF - if (var) - var->sp= lex->sphead; + if (var) + var->sp= lex->sphead; #endif - } - else - { - /* - The parser won't create select_result instance only - if it's an EXPLAIN. - */ - DBUG_ASSERT(lex->describe); - } - } - ; + } + else + { + /* + The parser won't create select_result instance only + if it's an EXPLAIN. + */ + DBUG_ASSERT(lex->describe); + } + } + ; into: - INTO - { - if (! Lex->parsing_options.allows_select_into) + INTO { - my_error(ER_VIEW_SELECT_CLAUSE, MYF(0), "INTO"); - MYSQL_YYABORT; + if (! Lex->parsing_options.allows_select_into) + { + my_error(ER_VIEW_SELECT_CLAUSE, MYF(0), "INTO"); + MYSQL_YYABORT; + } } - } - into_destination + into_destination ; into_destination: - OUTFILE TEXT_STRING_filesystem - { - LEX *lex= Lex; - lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - if (!(lex->exchange= new sql_exchange($2.str, 0)) || - !(lex->result= new select_export(lex->exchange))) - MYSQL_YYABORT; - } - opt_field_term opt_line_term - | DUMPFILE TEXT_STRING_filesystem - { - LEX *lex=Lex; - if (!lex->describe) - { - lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - if (!(lex->exchange= new sql_exchange($2.str,1))) - MYSQL_YYABORT; - if (!(lex->result= new select_dump(lex->exchange))) - MYSQL_YYABORT; - } - } + OUTFILE TEXT_STRING_filesystem + { + LEX *lex= Lex; + lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + if (!(lex->exchange= new sql_exchange($2.str, 0)) || + !(lex->result= new select_export(lex->exchange))) + MYSQL_YYABORT; + } + opt_field_term opt_line_term + | DUMPFILE TEXT_STRING_filesystem + { + LEX *lex=Lex; + if (!lex->describe) + { + lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + if (!(lex->exchange= new sql_exchange($2.str,1))) + MYSQL_YYABORT; + if (!(lex->result= new select_dump(lex->exchange))) + MYSQL_YYABORT; + } + } | select_var_list_init - { - Lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - } + { + Lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + } ; /* DO statement */ -do: DO_SYM - { - LEX *lex=Lex; - lex->sql_command = SQLCOM_DO; - mysql_init_select(lex); - } - expr_list - { - Lex->insert_list= $3; - } - ; +do: + DO_SYM + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_DO; + mysql_init_select(lex); + } + expr_list + { + Lex->insert_list= $3; + } + ; /* Drop : delete tables or index or user */ drop: - DROP opt_temporary table_or_tables if_exists table_list opt_restrict - { - LEX *lex=Lex; - lex->sql_command = SQLCOM_DROP_TABLE; - lex->drop_temporary= $2; - lex->drop_if_exists= $4; - } - | DROP INDEX_SYM ident ON table_ident {} - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_DROP_INDEX; - lex->alter_info.reset(); - lex->alter_info.flags= ALTER_DROP_INDEX; - lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::KEY, - $3.str)); - if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL, - TL_OPTION_UPDATING)) - MYSQL_YYABORT; - } - | DROP DATABASE if_exists ident - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_DROP_DB; - lex->drop_if_exists=$3; - lex->name= $4; - } - | DROP FUNCTION_SYM if_exists sp_name - { - LEX *lex=Lex; - if (lex->sphead) - { - my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"); - MYSQL_YYABORT; - } - lex->sql_command = SQLCOM_DROP_FUNCTION; - lex->drop_if_exists= $3; - lex->spname= $4; - } - | DROP PROCEDURE if_exists sp_name - { - LEX *lex=Lex; - if (lex->sphead) - { - my_error(ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE"); - MYSQL_YYABORT; - } - lex->sql_command = SQLCOM_DROP_PROCEDURE; - lex->drop_if_exists= $3; - lex->spname= $4; - } - | DROP USER clear_privileges user_list - { - Lex->sql_command = SQLCOM_DROP_USER; - } - | DROP VIEW_SYM if_exists table_list opt_restrict - { - LEX *lex= Lex; - lex->sql_command= SQLCOM_DROP_VIEW; - lex->drop_if_exists= $3; - } + DROP opt_temporary table_or_tables if_exists table_list opt_restrict + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_DROP_TABLE; + lex->drop_temporary= $2; + lex->drop_if_exists= $4; + } + | DROP INDEX_SYM ident ON table_ident {} + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_DROP_INDEX; + lex->alter_info.reset(); + lex->alter_info.flags= ALTER_DROP_INDEX; + lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::KEY, + $3.str)); + if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL, + TL_OPTION_UPDATING)) + MYSQL_YYABORT; + } + | DROP DATABASE if_exists ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_DROP_DB; + lex->drop_if_exists=$3; + lex->name= $4; + } + | DROP FUNCTION_SYM if_exists sp_name + { + LEX *lex=Lex; + if (lex->sphead) + { + my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"); + MYSQL_YYABORT; + } + lex->sql_command = SQLCOM_DROP_FUNCTION; + lex->drop_if_exists= $3; + lex->spname= $4; + } + | DROP PROCEDURE if_exists sp_name + { + LEX *lex=Lex; + if (lex->sphead) + { + my_error(ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE"); + MYSQL_YYABORT; + } + lex->sql_command = SQLCOM_DROP_PROCEDURE; + lex->drop_if_exists= $3; + lex->spname= $4; + } + | DROP USER clear_privileges user_list + { + Lex->sql_command = SQLCOM_DROP_USER; + } + | DROP VIEW_SYM if_exists table_list opt_restrict + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DROP_VIEW; + lex->drop_if_exists= $3; + } | DROP EVENT_SYM if_exists sp_name { Lex->drop_if_exists= $3; @@ -8258,7 +8642,7 @@ drop: lex->sql_command= SQLCOM_DROP_TRIGGER; lex->drop_if_exists= $3; lex->spname= $4; - } + } | DROP TABLESPACE tablespace_name opt_ts_engine opt_ts_wait { LEX *lex= Lex; @@ -8268,79 +8652,95 @@ drop: { 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; - } - ; + { + 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: - table_name - | table_list ',' table_name; + table_name + | table_list ',' table_name + ; table_name: + table_ident + { + if (!Select->add_table_to_list(YYTHD, $1, NULL, TL_OPTION_UPDATING)) + MYSQL_YYABORT; + } + ; + +table_alias_ref_list: + table_alias_ref + | table_alias_ref_list ',' table_alias_ref; + +table_alias_ref: table_ident { - if (!Select->add_table_to_list(YYTHD, $1, NULL, TL_OPTION_UPDATING)) + if (!Select->add_table_to_list(YYTHD, $1, NULL, + TL_OPTION_UPDATING | TL_OPTION_ALIAS, + Lex->lock_option )) MYSQL_YYABORT; } ; if_exists: - /* empty */ { $$= 0; } - | IF EXISTS { $$= 1; } - ; + /* empty */ { $$= 0; } + | IF EXISTS { $$= 1; } + ; opt_temporary: - /* empty */ { $$= 0; } - | TEMPORARY { $$= 1; } - ; + /* empty */ { $$= 0; } + | TEMPORARY { $$= 1; } + ; /* ** Insert : add new data to table */ insert: - INSERT - { - LEX *lex= Lex; - lex->sql_command= SQLCOM_INSERT; - lex->duplicates= DUP_ERROR; - mysql_init_select(lex); - /* for subselects */ - lex->lock_option= (using_update_log) ? TL_READ_NO_INSERT : TL_READ; - } insert_lock_option - opt_ignore insert2 - { - Select->set_lock_for_tables($3); - Lex->current_select= &Lex->select_lex; - } - insert_field_spec opt_insert_update - {} - ; + INSERT + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_INSERT; + lex->duplicates= DUP_ERROR; + mysql_init_select(lex); + /* for subselects */ + lex->lock_option= (using_update_log) ? TL_READ_NO_INSERT : TL_READ; + } + insert_lock_option + opt_ignore insert2 + { + Select->set_lock_for_tables($3); + Lex->current_select= &Lex->select_lex; + } + insert_field_spec opt_insert_update + {} + ; replace: - REPLACE - { - LEX *lex=Lex; - lex->sql_command = SQLCOM_REPLACE; - lex->duplicates= DUP_REPLACE; - mysql_init_select(lex); - } - replace_lock_option insert2 - { - Select->set_lock_for_tables($3); - Lex->current_select= &Lex->select_lex; - } - insert_field_spec - {} - ; + REPLACE + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_REPLACE; + lex->duplicates= DUP_REPLACE; + mysql_init_select(lex); + } + replace_lock_option insert2 + { + Select->set_lock_for_tables($3); + Lex->current_select= &Lex->select_lex; + } + insert_field_spec + {} + ; insert_lock_option: - /* empty */ + /* empty */ { #ifdef HAVE_QUERY_CACHE /* @@ -8353,298 +8753,321 @@ insert_lock_option: $$= TL_WRITE_CONCURRENT_INSERT; #endif } - | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; } - | DELAYED_SYM { $$= TL_WRITE_DELAYED; } - | HIGH_PRIORITY { $$= TL_WRITE; } - ; + | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; } + | DELAYED_SYM { $$= TL_WRITE_DELAYED; } + | HIGH_PRIORITY { $$= TL_WRITE; } + ; replace_lock_option: - opt_low_priority { $$= $1; } - | DELAYED_SYM { $$= TL_WRITE_DELAYED; }; + opt_low_priority { $$= $1; } + | DELAYED_SYM { $$= TL_WRITE_DELAYED; } + ; insert2: - INTO insert_table {} - | insert_table {}; + INTO insert_table {} + | insert_table {} + ; insert_table: - table_name - { - LEX *lex=Lex; - lex->field_list.empty(); - lex->many_values.empty(); - lex->insert_list=0; - }; + table_name + { + LEX *lex=Lex; + lex->field_list.empty(); + lex->many_values.empty(); + lex->insert_list=0; + }; insert_field_spec: - insert_values {} - | '(' ')' insert_values {} - | '(' fields ')' insert_values {} - | SET - { - LEX *lex=Lex; - if (!(lex->insert_list = new List_item) || - lex->many_values.push_back(lex->insert_list)) - MYSQL_YYABORT; - } - ident_eq_list; + insert_values {} + | '(' ')' insert_values {} + | '(' fields ')' insert_values {} + | SET + { + LEX *lex=Lex; + if (!(lex->insert_list = new List_item) || + lex->many_values.push_back(lex->insert_list)) + MYSQL_YYABORT; + } + ident_eq_list + ; fields: - fields ',' insert_ident { Lex->field_list.push_back($3); } - | insert_ident { Lex->field_list.push_back($1); }; + fields ',' insert_ident { Lex->field_list.push_back($3); } + | insert_ident { Lex->field_list.push_back($1); } + ; insert_values: - VALUES values_list {} - | VALUE_SYM values_list {} - | create_select { Select->set_braces(0);} union_clause {} - | '(' create_select ')' { Select->set_braces(1);} union_opt {} + VALUES values_list {} + | VALUE_SYM values_list {} + | create_select + { Select->set_braces(0);} + union_clause {} + | '(' create_select ')' + { Select->set_braces(1);} + union_opt {} ; values_list: - values_list ',' no_braces - | no_braces; + values_list ',' no_braces + | no_braces + ; ident_eq_list: - ident_eq_list ',' ident_eq_value - | - ident_eq_value; + ident_eq_list ',' ident_eq_value + | ident_eq_value + ; ident_eq_value: - simple_ident_nospvar equal expr_or_default - { - LEX *lex=Lex; - if (lex->field_list.push_back($1) || - lex->insert_list->push_back($3)) - MYSQL_YYABORT; - }; + simple_ident_nospvar equal expr_or_default + { + LEX *lex=Lex; + if (lex->field_list.push_back($1) || + lex->insert_list->push_back($3)) + MYSQL_YYABORT; + } + ; -equal: EQ {} - | SET_VAR {} - ; +equal: + EQ {} + | SET_VAR {} + ; opt_equal: - /* empty */ {} - | equal {} - ; + /* empty */ {} + | equal {} + ; no_braces: - '(' - { - if (!(Lex->insert_list = new List_item)) - MYSQL_YYABORT; - } - opt_values ')' - { - LEX *lex=Lex; - if (lex->many_values.push_back(lex->insert_list)) - MYSQL_YYABORT; - }; + '(' + { + if (!(Lex->insert_list = new List_item)) + MYSQL_YYABORT; + } + opt_values ')' + { + LEX *lex=Lex; + if (lex->many_values.push_back(lex->insert_list)) + MYSQL_YYABORT; + } + ; opt_values: - /* empty */ {} - | values; + /* empty */ {} + | values + ; values: - values ',' expr_or_default - { - if (Lex->insert_list->push_back($3)) - MYSQL_YYABORT; - } - | expr_or_default - { - if (Lex->insert_list->push_back($1)) - MYSQL_YYABORT; - } - ; + values ',' expr_or_default + { + if (Lex->insert_list->push_back($3)) + MYSQL_YYABORT; + } + | expr_or_default + { + if (Lex->insert_list->push_back($1)) + MYSQL_YYABORT; + } + ; expr_or_default: - expr { $$= $1;} - | DEFAULT {$$= new Item_default_value(Lex->current_context()); } - ; + expr { $$= $1;} + | DEFAULT {$$= new Item_default_value(Lex->current_context()); } + ; opt_insert_update: - /* empty */ - | ON DUPLICATE_SYM { Lex->duplicates= DUP_UPDATE; } + /* empty */ + | ON DUPLICATE_SYM { Lex->duplicates= DUP_UPDATE; } KEY_SYM UPDATE_SYM insert_update_list ; /* Update rows in a table */ update: - UPDATE_SYM - { - LEX *lex= Lex; - mysql_init_select(lex); - lex->sql_command= SQLCOM_UPDATE; - lex->lock_option= TL_UNLOCK; /* Will be set later */ - lex->duplicates= DUP_ERROR; - } - opt_low_priority opt_ignore join_table_list - SET update_list - { - LEX *lex= Lex; - if (lex->select_lex.table_list.elements > 1) - lex->sql_command= SQLCOM_UPDATE_MULTI; - else if (lex->select_lex.get_table_list()->derived) - { - /* 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"); - MYSQL_YYABORT; - } - /* - In case of multi-update setting write lock for all tables may - be too pessimistic. We will decrease lock level if possible in - mysql_multi_update(). - */ - Select->set_lock_for_tables($3); - } - where_clause opt_order_clause delete_limit_clause {} - ; + UPDATE_SYM + { + LEX *lex= Lex; + mysql_init_select(lex); + lex->sql_command= SQLCOM_UPDATE; + lex->lock_option= TL_UNLOCK; /* Will be set later */ + lex->duplicates= DUP_ERROR; + } + opt_low_priority opt_ignore join_table_list + SET update_list + { + LEX *lex= Lex; + if (lex->select_lex.table_list.elements > 1) + lex->sql_command= SQLCOM_UPDATE_MULTI; + else if (lex->select_lex.get_table_list()->derived) + { + /* 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"); + MYSQL_YYABORT; + } + /* + In case of multi-update setting write lock for all tables may + be too pessimistic. We will decrease lock level if possible in + mysql_multi_update(). + */ + Select->set_lock_for_tables($3); + } + where_clause opt_order_clause delete_limit_clause {} + ; update_list: - update_list ',' update_elem - | update_elem; + update_list ',' update_elem + | update_elem + ; update_elem: - simple_ident_nospvar equal expr_or_default - { - if (add_item_to_list(YYTHD, $1) || add_value_to_list(YYTHD, $3)) - MYSQL_YYABORT; - }; + simple_ident_nospvar equal expr_or_default + { + if (add_item_to_list(YYTHD, $1) || add_value_to_list(YYTHD, $3)) + MYSQL_YYABORT; + } + ; insert_update_list: - insert_update_list ',' insert_update_elem - | insert_update_elem; + insert_update_list ',' insert_update_elem + | insert_update_elem + ; insert_update_elem: - simple_ident_nospvar equal expr_or_default - { - LEX *lex= Lex; - if (lex->update_list.push_back($1) || - lex->value_list.push_back($3)) - MYSQL_YYABORT; - }; + simple_ident_nospvar equal expr_or_default + { + LEX *lex= Lex; + if (lex->update_list.push_back($1) || + lex->value_list.push_back($3)) + MYSQL_YYABORT; + } + ; opt_low_priority: - /* empty */ { $$= TL_WRITE_DEFAULT; } - | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }; + /* empty */ { $$= TL_WRITE_DEFAULT; } + | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; } + ; /* Delete rows from a table */ delete: - DELETE_SYM - { - LEX *lex= Lex; - lex->sql_command= SQLCOM_DELETE; - mysql_init_select(lex); - lex->lock_option= TL_WRITE_DEFAULT; - lex->ignore= 0; - lex->select_lex.init_order(); - } - opt_delete_options single_multi {} - ; + DELETE_SYM + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DELETE; + mysql_init_select(lex); + lex->lock_option= TL_WRITE_DEFAULT; + lex->ignore= 0; + lex->select_lex.init_order(); + } + opt_delete_options single_multi {} + ; single_multi: - FROM table_ident - { - if (!Select->add_table_to_list(YYTHD, $2, NULL, TL_OPTION_UPDATING, - Lex->lock_option)) - MYSQL_YYABORT; - } - where_clause opt_order_clause - delete_limit_clause {} - | table_wild_list - { mysql_init_multi_delete(Lex); } + FROM table_ident + { + if (!Select->add_table_to_list(YYTHD, $2, NULL, TL_OPTION_UPDATING, + Lex->lock_option)) + MYSQL_YYABORT; + } + where_clause opt_order_clause + delete_limit_clause {} + | table_wild_list + { mysql_init_multi_delete(Lex); } FROM join_table_list where_clause { if (multi_delete_set_locks_and_link_aux_tables(Lex)) MYSQL_YYABORT; } - | FROM table_wild_list - { mysql_init_multi_delete(Lex); } - USING join_table_list where_clause + | FROM table_alias_ref_list + { mysql_init_multi_delete(Lex); } + USING join_table_list where_clause { if (multi_delete_set_locks_and_link_aux_tables(Lex)) MYSQL_YYABORT; } - ; + ; table_wild_list: - table_wild_one {} - | table_wild_list ',' table_wild_one {}; + table_wild_one {} + | table_wild_list ',' table_wild_one {} + ; table_wild_one: - ident opt_wild opt_table_alias - { - if (!Select->add_table_to_list(YYTHD, new Table_ident($1), $3, - TL_OPTION_UPDATING | - TL_OPTION_ALIAS, Lex->lock_option)) - MYSQL_YYABORT; - } - | ident '.' ident opt_wild opt_table_alias - { - if (!Select->add_table_to_list(YYTHD, - new Table_ident(YYTHD, $1, $3, 0), - $5, - TL_OPTION_UPDATING | - TL_OPTION_ALIAS, - Lex->lock_option)) - MYSQL_YYABORT; - } - ; + ident opt_wild opt_table_alias + { + if (!Select->add_table_to_list(YYTHD, new Table_ident($1), + $3, + TL_OPTION_UPDATING | TL_OPTION_ALIAS, + Lex->lock_option)) + MYSQL_YYABORT; + } + | ident '.' ident opt_wild opt_table_alias + { + if (!Select->add_table_to_list(YYTHD, + new Table_ident(YYTHD, $1, $3, 0), + $5, + TL_OPTION_UPDATING | TL_OPTION_ALIAS, + Lex->lock_option)) + MYSQL_YYABORT; + } + ; opt_wild: - /* empty */ {} - | '.' '*' {}; - + /* empty */ {} + | '.' '*' {} + ; opt_delete_options: - /* empty */ {} - | opt_delete_option opt_delete_options {}; + /* empty */ {} + | opt_delete_option opt_delete_options {} + ; opt_delete_option: - QUICK { Select->options|= OPTION_QUICK; } - | LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; } - | IGNORE_SYM { Lex->ignore= 1; }; + QUICK { Select->options|= OPTION_QUICK; } + | LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; } + | IGNORE_SYM { Lex->ignore= 1; } + ; truncate: - TRUNCATE_SYM opt_table_sym table_name - { - LEX* lex= Lex; - lex->sql_command= SQLCOM_TRUNCATE; - lex->select_lex.options= 0; - lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED; - lex->select_lex.init_order(); - } - ; + TRUNCATE_SYM opt_table_sym table_name + { + LEX* lex= Lex; + lex->sql_command= SQLCOM_TRUNCATE; + lex->select_lex.options= 0; + lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED; + lex->select_lex.init_order(); + } + ; opt_table_sym: - /* empty */ - | TABLE_SYM; + /* empty */ + | TABLE_SYM + ; /* Show things */ -show: SHOW - { - LEX *lex=Lex; - lex->wild=0; - lex->lock_option= TL_READ; - mysql_init_select(lex); - lex->current_select->parsing_place= SELECT_LIST; - bzero((char*) &lex->create_info,sizeof(lex->create_info)); - } - show_param - {} - ; +show: + SHOW + { + LEX *lex=Lex; + lex->wild=0; + lex->lock_option= TL_READ; + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; + bzero((char*) &lex->create_info,sizeof(lex->create_info)); + } + show_param + {} + ; show_param: - DATABASES wild_and_where - { - LEX *lex= Lex; - lex->sql_command= SQLCOM_SHOW_DATABASES; - if (prepare_schema_table(YYTHD, lex, 0, SCH_SCHEMATA)) - MYSQL_YYABORT; - } + DATABASES wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_DATABASES; + if (prepare_schema_table(YYTHD, lex, 0, SCH_SCHEMATA)) + MYSQL_YYABORT; + } | opt_full TABLES opt_db wild_and_where { LEX *lex= Lex; @@ -8678,110 +9101,115 @@ show_param: MYSQL_YYABORT; } | OPEN_SYM TABLES opt_db wild_and_where - { - LEX *lex= Lex; + { + LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_OPEN_TABLES; - lex->select_lex.db= $3; + lex->select_lex.db= $3; if (prepare_schema_table(YYTHD, lex, 0, SCH_OPEN_TABLES)) MYSQL_YYABORT; - } + } | opt_full PLUGIN_SYM - { - LEX *lex= Lex; - WARN_DEPRECATED(yythd, "5.2", "SHOW PLUGIN", "'SHOW PLUGINS'"); + { + LEX *lex= Lex; + WARN_DEPRECATED(yythd, "5.2", "SHOW PLUGIN", "'SHOW PLUGINS'"); lex->sql_command= SQLCOM_SHOW_PLUGINS; if (prepare_schema_table(YYTHD, lex, 0, SCH_PLUGINS)) MYSQL_YYABORT; - } + } | PLUGINS_SYM - { - LEX *lex= Lex; + { + LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_PLUGINS; if (prepare_schema_table(YYTHD, lex, 0, SCH_PLUGINS)) MYSQL_YYABORT; - } - | ENGINE_SYM known_storage_engines show_engine_param - { Lex->create_info.db_type= $2; } - | ENGINE_SYM ALL show_engine_param - { Lex->create_info.db_type= NULL; } - | opt_full COLUMNS from_or_in table_ident opt_db wild_and_where - { - LEX *lex= Lex; - lex->sql_command= SQLCOM_SHOW_FIELDS; - if ($5) - $4->change_db($5); - if (prepare_schema_table(YYTHD, lex, $4, SCH_COLUMNS)) - 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 - AND_SYM MASTER_SERVER_ID_SYM EQ - ulong_num - { - Lex->sql_command = SQLCOM_SHOW_NEW_MASTER; - Lex->mi.log_file_name = $8.str; - Lex->mi.pos = $12; - Lex->mi.server_id = $16; + } + | ENGINE_SYM known_storage_engines show_engine_param + { Lex->create_info.db_type= $2; } + | ENGINE_SYM ALL show_engine_param + { Lex->create_info.db_type= NULL; } + | opt_full COLUMNS from_or_in table_ident opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_FIELDS; + if ($5) + $4->change_db($5); + if (prepare_schema_table(YYTHD, lex, $4, SCH_COLUMNS)) + MYSQL_YYABORT; + } + | NEW_SYM MASTER_SYM FOR_SYM SLAVE + WITH MASTER_LOG_FILE_SYM EQ + TEXT_STRING_sys /* $8 */ + AND_SYM MASTER_LOG_POS_SYM EQ + ulonglong_num /* $12 */ + AND_SYM MASTER_SERVER_ID_SYM EQ + ulong_num /* $16 */ + { + Lex->sql_command = SQLCOM_SHOW_NEW_MASTER; + Lex->mi.log_file_name = $8.str; + Lex->mi.pos = $12; + Lex->mi.server_id = $16; } | master_or_binary LOGS_SYM { - Lex->sql_command = SQLCOM_SHOW_BINLOGS; + Lex->sql_command = SQLCOM_SHOW_BINLOGS; } | SLAVE HOSTS_SYM { - Lex->sql_command = SQLCOM_SHOW_SLAVE_HOSTS; + Lex->sql_command = SQLCOM_SHOW_SLAVE_HOSTS; } | BINLOG_SYM EVENTS_SYM binlog_in binlog_from { - LEX *lex= Lex; - lex->sql_command= SQLCOM_SHOW_BINLOG_EVENTS; + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_BINLOG_EVENTS; } opt_limit_clause_init | keys_or_index from_or_in table_ident opt_db where_clause { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_KEYS; - if ($4) - $3->change_db($4); + if ($4) + $3->change_db($4); if (prepare_schema_table(YYTHD, lex, $3, SCH_STATISTICS)) MYSQL_YYABORT; - } - | COLUMN_SYM TYPES_SYM - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_SHOW_COLUMN_TYPES; - } - | TABLE_SYM TYPES_SYM - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_SHOW_STORAGE_ENGINES; - WARN_DEPRECATED(yythd, "5.2", "SHOW TABLE TYPES", "'SHOW [STORAGE] ENGINES'"); - } - | opt_storage ENGINES_SYM - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_SHOW_STORAGE_ENGINES; + } + | COLUMN_SYM TYPES_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_COLUMN_TYPES; + } + | TABLE_SYM TYPES_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_STORAGE_ENGINES; + WARN_DEPRECATED(yythd, "5.2", "SHOW TABLE TYPES", "'SHOW [STORAGE] ENGINES'"); if (prepare_schema_table(YYTHD, lex, 0, SCH_ENGINES)) MYSQL_YYABORT; - } - | AUTHORS_SYM - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_SHOW_AUTHORS; - } - | CONTRIBUTORS_SYM - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_SHOW_CONTRIBUTORS; - } - | PRIVILEGES - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_SHOW_PRIVILEGES; - } + } + | opt_storage ENGINES_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_STORAGE_ENGINES; + if (prepare_schema_table(YYTHD, lex, 0, SCH_ENGINES)) + MYSQL_YYABORT; + } + | AUTHORS_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_AUTHORS; + } + | CONTRIBUTORS_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_CONTRIBUTORS; + } + | PRIVILEGES + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_PRIVILEGES; + } | COUNT_SYM '(' '*' ')' WARNINGS { (void) create_select_for_variable("warning_count"); } | COUNT_SYM '(' '*' ')' ERRORS - { (void) create_select_for_variable("error_count"); } + { (void) create_select_for_variable("error_count"); } | WARNINGS opt_limit_clause_init { Lex->sql_command = SQLCOM_SHOW_WARNS;} | ERRORS opt_limit_clause_init @@ -8793,7 +9221,7 @@ show_param: lex->option_type= $1; if (prepare_schema_table(YYTHD, lex, 0, SCH_STATUS)) MYSQL_YYABORT; - } + } | INNOBASE_SYM STATUS_SYM { LEX *lex= Lex; @@ -8801,33 +9229,33 @@ show_param: if (!(lex->create_info.db_type= ha_resolve_by_legacy_type(YYTHD, DB_TYPE_INNODB))) { - my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), "InnoDB"); - MYSQL_YYABORT; + my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), "InnoDB"); + MYSQL_YYABORT; } WARN_DEPRECATED(yythd, "5.2", "SHOW INNODB STATUS", "'SHOW ENGINE INNODB STATUS'"); - } + } | MUTEX_SYM STATUS_SYM { - LEX *lex= Lex; + LEX *lex= Lex; lex->sql_command = SQLCOM_SHOW_ENGINE_MUTEX; if (!(lex->create_info.db_type= ha_resolve_by_legacy_type(YYTHD, DB_TYPE_INNODB))) { - my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), "InnoDB"); - MYSQL_YYABORT; + my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), "InnoDB"); + MYSQL_YYABORT; } WARN_DEPRECATED(yythd, "5.2", "SHOW MUTEX STATUS", "'SHOW ENGINE INNODB MUTEX'"); - } - | opt_full PROCESSLIST_SYM - { Lex->sql_command= SQLCOM_SHOW_PROCESSLIST;} + } + | opt_full PROCESSLIST_SYM + { Lex->sql_command= SQLCOM_SHOW_PROCESSLIST;} | opt_var_type VARIABLES wild_and_where - { + { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_VARIABLES; lex->option_type= $1; if (prepare_schema_table(YYTHD, lex, 0, SCH_VARIABLES)) MYSQL_YYABORT; - } + } | charset wild_and_where { LEX *lex= Lex; @@ -8842,92 +9270,92 @@ show_param: if (prepare_schema_table(YYTHD, lex, 0, SCH_COLLATIONS)) MYSQL_YYABORT; } - | GRANTS - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_SHOW_GRANTS; - LEX_USER *curr_user; + | GRANTS + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_GRANTS; + LEX_USER *curr_user; if (!(curr_user= (LEX_USER*) lex->thd->alloc(sizeof(st_lex_user)))) MYSQL_YYABORT; bzero(curr_user, sizeof(st_lex_user)); - lex->grant_user= curr_user; - } - | GRANTS FOR_SYM user - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_SHOW_GRANTS; - lex->grant_user=$3; - lex->grant_user->password=null_lex_str; - } - | CREATE DATABASE opt_if_not_exists ident - { - Lex->sql_command=SQLCOM_SHOW_CREATE_DB; - Lex->create_info.options=$3; - Lex->name= $4; - } + lex->grant_user= curr_user; + } + | GRANTS FOR_SYM user + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_GRANTS; + lex->grant_user=$3; + lex->grant_user->password=null_lex_str; + } + | CREATE DATABASE opt_if_not_exists ident + { + Lex->sql_command=SQLCOM_SHOW_CREATE_DB; + Lex->create_info.options=$3; + 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)) - MYSQL_YYABORT; + lex->sql_command = SQLCOM_SHOW_CREATE; + if (!lex->select_lex.add_table_to_list(YYTHD, $3, NULL,0)) + MYSQL_YYABORT; lex->only_view= 0; - lex->create_info.storage_media= HA_SM_DEFAULT; - } + 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)) - MYSQL_YYABORT; + lex->sql_command = SQLCOM_SHOW_CREATE; + if (!lex->select_lex.add_table_to_list(YYTHD, $3, NULL, 0)) + MYSQL_YYABORT; lex->only_view= 1; - } + } | MASTER_SYM STATUS_SYM { - Lex->sql_command = SQLCOM_SHOW_MASTER_STAT; + Lex->sql_command = SQLCOM_SHOW_MASTER_STAT; } | SLAVE STATUS_SYM { - Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; - } - | CREATE PROCEDURE sp_name - { - LEX *lex= Lex; - - lex->sql_command = SQLCOM_SHOW_CREATE_PROC; - lex->spname= $3; - } - | CREATE FUNCTION_SYM sp_name - { - LEX *lex= Lex; - - lex->sql_command = SQLCOM_SHOW_CREATE_FUNC; - lex->spname= $3; - } - | CREATE TRIGGER_SYM sp_name - { - LEX *lex= Lex; - lex->sql_command= SQLCOM_SHOW_CREATE_TRIGGER; - lex->spname= $3; - } - | PROCEDURE STATUS_SYM wild_and_where - { + Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; + } + | CREATE PROCEDURE sp_name + { + LEX *lex= Lex; + + lex->sql_command = SQLCOM_SHOW_CREATE_PROC; + lex->spname= $3; + } + | CREATE FUNCTION_SYM sp_name + { + LEX *lex= Lex; + + lex->sql_command = SQLCOM_SHOW_CREATE_FUNC; + lex->spname= $3; + } + | CREATE TRIGGER_SYM sp_name + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_CREATE_TRIGGER; + lex->spname= $3; + } + | PROCEDURE STATUS_SYM wild_and_where + { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_STATUS_PROC; - if (!sp_add_to_query_tables(YYTHD, lex, "mysql", "proc", TL_READ)) - MYSQL_YYABORT; + if (!sp_add_to_query_tables(YYTHD, lex, "mysql", "proc", TL_READ)) + MYSQL_YYABORT; if (prepare_schema_table(YYTHD, lex, 0, SCH_PROCEDURES)) MYSQL_YYABORT; - } - | FUNCTION_SYM STATUS_SYM wild_and_where - { + } + | 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)) - MYSQL_YYABORT; + if (!sp_add_to_query_tables(YYTHD, lex, "mysql", "proc", TL_READ)) + MYSQL_YYABORT; if (prepare_schema_table(YYTHD, lex, 0, SCH_PROCEDURES)) MYSQL_YYABORT; - } + } | PROCEDURE CODE_SYM sp_name { #ifdef DBUG_OFF @@ -8935,7 +9363,7 @@ show_param: MYSQL_YYABORT; #else Lex->sql_command= SQLCOM_SHOW_PROC_CODE; - Lex->spname= $3; + Lex->spname= $3; #endif } | FUNCTION_SYM CODE_SYM sp_name @@ -8945,7 +9373,7 @@ show_param: MYSQL_YYABORT; #else Lex->sql_command= SQLCOM_SHOW_FUNC_CODE; - Lex->spname= $3; + Lex->spname= $3; #endif } | CREATE EVENT_SYM sp_name @@ -8953,290 +9381,328 @@ show_param: Lex->spname= $3; Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT; } - ; + ; show_engine_param: - STATUS_SYM - { Lex->sql_command= SQLCOM_SHOW_ENGINE_STATUS; } - | MUTEX_SYM - { Lex->sql_command= SQLCOM_SHOW_ENGINE_MUTEX; } - | LOGS_SYM - { Lex->sql_command= SQLCOM_SHOW_ENGINE_LOGS; }; + STATUS_SYM + { Lex->sql_command= SQLCOM_SHOW_ENGINE_STATUS; } + | MUTEX_SYM + { Lex->sql_command= SQLCOM_SHOW_ENGINE_MUTEX; } + | LOGS_SYM + { Lex->sql_command= SQLCOM_SHOW_ENGINE_LOGS; } + ; master_or_binary: - MASTER_SYM - | BINARY; + MASTER_SYM + | BINARY + ; opt_storage: - /* empty */ - | STORAGE_SYM; + /* empty */ + | STORAGE_SYM + ; opt_db: - /* empty */ { $$= 0; } - | from_or_in ident { $$= $2.str; }; + /* empty */ { $$= 0; } + | from_or_in ident { $$= $2.str; } + ; opt_full: - /* empty */ { Lex->verbose=0; } - | FULL { Lex->verbose=1; }; + /* empty */ { Lex->verbose=0; } + | FULL { Lex->verbose=1; } + ; from_or_in: - FROM - | IN_SYM; + FROM + | IN_SYM + ; binlog_in: - /* empty */ { Lex->mi.log_file_name = 0; } - | IN_SYM TEXT_STRING_sys { Lex->mi.log_file_name = $2.str; }; + /* empty */ { Lex->mi.log_file_name = 0; } + | IN_SYM TEXT_STRING_sys { Lex->mi.log_file_name = $2.str; } + ; binlog_from: - /* empty */ { Lex->mi.pos = 4; /* skip magic number */ } - | FROM ulonglong_num { Lex->mi.pos = $2; }; + /* empty */ { Lex->mi.pos = 4; /* skip magic number */ } + | FROM ulonglong_num { Lex->mi.pos = $2; } + ; wild_and_where: - /* empty */ - | LIKE TEXT_STRING_sys - { Lex->wild= new (YYTHD->mem_root) String($2.str, $2.length, - system_charset_info); } - | WHERE expr - { - Select->where= $2; - if ($2) - $2->top_level_item(); - } - ; - + /* empty */ + | LIKE TEXT_STRING_sys + { + Lex->wild= new (YYTHD->mem_root) String($2.str, $2.length, + system_charset_info); + } + | WHERE expr + { + Select->where= $2; + if ($2) + $2->top_level_item(); + } + ; /* A Oracle compatible synonym for show */ describe: - describe_command table_ident - { - LEX *lex= Lex; - lex->lock_option= TL_READ; - mysql_init_select(lex); - lex->current_select->parsing_place= SELECT_LIST; - lex->sql_command= SQLCOM_SHOW_FIELDS; - lex->select_lex.db= 0; - lex->verbose= 0; - if (prepare_schema_table(YYTHD, lex, $2, SCH_COLUMNS)) - MYSQL_YYABORT; - } - opt_describe_column {} - | describe_command opt_extended_describe - { Lex->describe|= DESCRIBE_NORMAL; } - select - { - LEX *lex=Lex; - lex->select_lex.options|= SELECT_DESCRIBE; - } - ; + describe_command table_ident + { + LEX *lex= Lex; + lex->lock_option= TL_READ; + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; + lex->sql_command= SQLCOM_SHOW_FIELDS; + lex->select_lex.db= 0; + lex->verbose= 0; + if (prepare_schema_table(YYTHD, lex, $2, SCH_COLUMNS)) + MYSQL_YYABORT; + } + opt_describe_column {} + | describe_command opt_extended_describe + { Lex->describe|= DESCRIBE_NORMAL; } + select + { + LEX *lex=Lex; + lex->select_lex.options|= SELECT_DESCRIBE; + } + ; describe_command: - DESC - | DESCRIBE; + DESC + | DESCRIBE + ; opt_extended_describe: - /* empty */ {} - | EXTENDED_SYM { Lex->describe|= DESCRIBE_EXTENDED; } - | PARTITIONS_SYM { Lex->describe|= DESCRIBE_PARTITIONS; } - ; - + /* empty */ {} + | EXTENDED_SYM { Lex->describe|= DESCRIBE_EXTENDED; } + | PARTITIONS_SYM { Lex->describe|= DESCRIBE_PARTITIONS; } + ; opt_describe_column: - /* empty */ {} - | text_string { Lex->wild= $1; } - | ident - { Lex->wild= new (YYTHD->mem_root) String((const char*) $1.str,$1.length,system_charset_info); }; + /* empty */ {} + | text_string { Lex->wild= $1; } + | ident + { + Lex->wild= new (YYTHD->mem_root) String((const char*) $1.str, + $1.length, + system_charset_info); + } + ; /* flush things */ flush: - FLUSH_SYM opt_no_write_to_binlog - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_FLUSH; - lex->type= 0; - lex->no_write_to_binlog= $2; - } - flush_options - {} - ; + FLUSH_SYM opt_no_write_to_binlog + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_FLUSH; + lex->type= 0; + lex->no_write_to_binlog= $2; + } + flush_options + {} + ; flush_options: - flush_options ',' flush_option - | flush_option; + flush_options ',' flush_option + | flush_option + ; flush_option: - table_or_tables { Lex->type|= REFRESH_TABLES; } opt_table_list {} - | TABLES WITH READ_SYM LOCK_SYM { Lex->type|= REFRESH_TABLES | REFRESH_READ_LOCK; } - | QUERY_SYM CACHE_SYM { Lex->type|= REFRESH_QUERY_CACHE_FREE; } - | HOSTS_SYM { Lex->type|= REFRESH_HOSTS; } - | PRIVILEGES { Lex->type|= REFRESH_GRANT; } - | LOGS_SYM { Lex->type|= REFRESH_LOG; } - | STATUS_SYM { Lex->type|= REFRESH_STATUS; } - | SLAVE { Lex->type|= REFRESH_SLAVE; } - | MASTER_SYM { Lex->type|= REFRESH_MASTER; } - | DES_KEY_FILE { Lex->type|= REFRESH_DES_KEY_FILE; } - | RESOURCES { Lex->type|= REFRESH_USER_RESOURCES; }; + table_or_tables + { Lex->type|= REFRESH_TABLES; } + opt_table_list {} + | TABLES WITH READ_SYM LOCK_SYM + { Lex->type|= REFRESH_TABLES | REFRESH_READ_LOCK; } + | QUERY_SYM CACHE_SYM + { Lex->type|= REFRESH_QUERY_CACHE_FREE; } + | HOSTS_SYM + { Lex->type|= REFRESH_HOSTS; } + | PRIVILEGES + { Lex->type|= REFRESH_GRANT; } + | LOGS_SYM + { Lex->type|= REFRESH_LOG; } + | STATUS_SYM + { Lex->type|= REFRESH_STATUS; } + | SLAVE + { Lex->type|= REFRESH_SLAVE; } + | MASTER_SYM + { Lex->type|= REFRESH_MASTER; } + | DES_KEY_FILE + { Lex->type|= REFRESH_DES_KEY_FILE; } + | RESOURCES + { Lex->type|= REFRESH_USER_RESOURCES; } + ; opt_table_list: - /* empty */ {;} - | table_list {;}; + /* empty */ {} + | table_list {} + ; reset: - RESET_SYM - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_RESET; lex->type=0; - } reset_options - {} - ; + RESET_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_RESET; lex->type=0; + } + reset_options + {} + ; reset_options: - reset_options ',' reset_option - | reset_option; + reset_options ',' reset_option + | reset_option + ; reset_option: - SLAVE { Lex->type|= REFRESH_SLAVE; } + SLAVE { Lex->type|= REFRESH_SLAVE; } | MASTER_SYM { Lex->type|= REFRESH_MASTER; } - | QUERY_SYM CACHE_SYM { Lex->type|= REFRESH_QUERY_CACHE;}; + | QUERY_SYM CACHE_SYM { Lex->type|= REFRESH_QUERY_CACHE;} + ; purge: - PURGE - { - LEX *lex=Lex; - lex->type=0; - lex->sql_command = SQLCOM_PURGE; - } purge_options - {} - ; + PURGE + { + LEX *lex=Lex; + lex->type=0; + lex->sql_command = SQLCOM_PURGE; + } + purge_options + {} + ; purge_options: - master_or_binary LOGS_SYM purge_option - ; + master_or_binary LOGS_SYM purge_option + ; purge_option: - TO_SYM TEXT_STRING_sys - { - Lex->to_log = $2.str; - } - | BEFORE_SYM expr - { - LEX *lex= Lex; - lex->value_list.empty(); - lex->value_list.push_front($2); - lex->sql_command= SQLCOM_PURGE_BEFORE; - } - ; + TO_SYM TEXT_STRING_sys + { + Lex->to_log = $2.str; + } + | BEFORE_SYM expr + { + LEX *lex= Lex; + lex->value_list.empty(); + lex->value_list.push_front($2); + lex->sql_command= SQLCOM_PURGE_BEFORE; + } + ; /* kill threads */ kill: - KILL_SYM kill_option expr - { - LEX *lex=Lex; - lex->value_list.empty(); - lex->value_list.push_front($3); - lex->sql_command= SQLCOM_KILL; - }; + KILL_SYM kill_option expr + { + LEX *lex=Lex; + lex->value_list.empty(); + lex->value_list.push_front($3); + lex->sql_command= SQLCOM_KILL; + } + ; kill_option: - /* empty */ { Lex->type= 0; } - | CONNECTION_SYM { Lex->type= 0; } - | QUERY_SYM { Lex->type= ONLY_KILL_QUERY; } + /* empty */ { Lex->type= 0; } + | CONNECTION_SYM { Lex->type= 0; } + | QUERY_SYM { Lex->type= ONLY_KILL_QUERY; } ; /* change database */ -use: USE_SYM ident - { - LEX *lex=Lex; - lex->sql_command=SQLCOM_CHANGE_DB; - lex->select_lex.db= $2.str; - }; +use: + USE_SYM ident + { + LEX *lex=Lex; + lex->sql_command=SQLCOM_CHANGE_DB; + lex->select_lex.db= $2.str; + } + ; /* import, export of files */ -load: LOAD DATA_SYM - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - Lex_input_stream *lip= thd->m_lip; +load: + LOAD DATA_SYM + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; - if (lex->sphead) - { - my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD DATA"); - MYSQL_YYABORT; - } - lex->fname_start= lip->get_ptr(); - } - load_data - {} - | - LOAD TABLE_SYM table_ident FROM MASTER_SYM - { - LEX *lex=Lex; - WARN_DEPRECATED(yythd, "5.2", "LOAD TABLE FROM MASTER", - "MySQL Administrator (mysqldump, mysql)"); - if (lex->sphead) - { - my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD TABLE"); - MYSQL_YYABORT; - } - lex->sql_command = SQLCOM_LOAD_MASTER_TABLE; - if (!Select->add_table_to_list(YYTHD, $3, NULL, TL_OPTION_UPDATING)) - MYSQL_YYABORT; - }; + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD DATA"); + MYSQL_YYABORT; + } + lex->fname_start= lip->get_ptr(); + } + load_data + {} + | LOAD TABLE_SYM table_ident FROM MASTER_SYM + { + LEX *lex=Lex; + WARN_DEPRECATED(yythd, "5.2", "LOAD TABLE FROM MASTER", + "MySQL Administrator (mysqldump, mysql)"); + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD TABLE"); + MYSQL_YYABORT; + } + lex->sql_command = SQLCOM_LOAD_MASTER_TABLE; + if (!Select->add_table_to_list(YYTHD, $3, NULL, TL_OPTION_UPDATING)) + MYSQL_YYABORT; + } + ; load_data: - load_data_lock opt_local INFILE TEXT_STRING_filesystem - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_LOAD; - lex->lock_option= $1; - lex->local_file= $2; - lex->duplicates= DUP_ERROR; - lex->ignore= 0; - if (!(lex->exchange= new sql_exchange($4.str, 0))) - MYSQL_YYABORT; - } - opt_duplicate INTO - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - Lex_input_stream *lip= thd->m_lip; - lex->fname_end= lip->get_ptr(); - } - TABLE_SYM table_ident - { - LEX *lex=Lex; - if (!Select->add_table_to_list(YYTHD, $10, NULL, TL_OPTION_UPDATING, - lex->lock_option)) - 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 - {} - | - FROM MASTER_SYM - { - Lex->sql_command = SQLCOM_LOAD_MASTER_DATA; - WARN_DEPRECATED(yythd, "5.2", "LOAD DATA FROM MASTER", - "mysqldump or future " - "BACKUP/RESTORE DATABASE facility"); - }; + load_data_lock opt_local INFILE TEXT_STRING_filesystem + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_LOAD; + lex->lock_option= $1; + lex->local_file= $2; + lex->duplicates= DUP_ERROR; + lex->ignore= 0; + if (!(lex->exchange= new sql_exchange($4.str, 0))) + MYSQL_YYABORT; + } + opt_duplicate INTO + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; + lex->fname_end= lip->get_ptr(); + } + TABLE_SYM table_ident + { + LEX *lex=Lex; + if (!Select->add_table_to_list(YYTHD, $10, NULL, TL_OPTION_UPDATING, + lex->lock_option)) + 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 + {} + | FROM MASTER_SYM + { + Lex->sql_command = SQLCOM_LOAD_MASTER_DATA; + WARN_DEPRECATED(yythd, "5.2", "LOAD DATA FROM MASTER", + "mysqldump or future " + "BACKUP/RESTORE DATABASE facility"); + } + ; opt_local: - /* empty */ { $$=0;} - | LOCAL_SYM { $$=1;}; + /* empty */ { $$=0;} + | LOCAL_SYM { $$=1;} + ; load_data_lock: - /* empty */ { $$= TL_WRITE_DEFAULT; } - | CONCURRENT + /* empty */ { $$= TL_WRITE_DEFAULT; } + | CONCURRENT { #ifdef HAVE_QUERY_CACHE /* @@ -9248,35 +9714,38 @@ load_data_lock: #endif $$= TL_WRITE_CONCURRENT_INSERT; } - | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }; - + | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; } + ; opt_duplicate: - /* empty */ { Lex->duplicates=DUP_ERROR; } - | REPLACE { Lex->duplicates=DUP_REPLACE; } - | IGNORE_SYM { Lex->ignore= 1; }; + /* empty */ { Lex->duplicates=DUP_ERROR; } + | REPLACE { Lex->duplicates=DUP_REPLACE; } + | IGNORE_SYM { Lex->ignore= 1; } + ; opt_field_term: - /* empty */ - | COLUMNS field_term_list; + /* empty */ + | COLUMNS field_term_list + ; field_term_list: - field_term_list field_term - | field_term; + field_term_list field_term + | field_term + ; field_term: - TERMINATED BY text_string + TERMINATED BY text_string { DBUG_ASSERT(Lex->exchange != 0); Lex->exchange->field_term= $3; } - | OPTIONALLY ENCLOSED BY text_string - { + | OPTIONALLY ENCLOSED BY text_string + { LEX *lex= Lex; DBUG_ASSERT(lex->exchange != 0); lex->exchange->enclosed= $4; lex->exchange->opt_enclosed= 1; - } + } | ENCLOSED BY text_string { DBUG_ASSERT(Lex->exchange != 0); @@ -9286,18 +9755,21 @@ field_term: { DBUG_ASSERT(Lex->exchange != 0); Lex->exchange->escaped= $3; - }; + } + ; opt_line_term: - /* empty */ - | LINES line_term_list; + /* empty */ + | LINES line_term_list + ; line_term_list: - line_term_list line_term - | line_term; + line_term_list line_term + | line_term + ; line_term: - TERMINATED BY text_string + TERMINATED BY text_string { DBUG_ASSERT(Lex->exchange != 0); Lex->exchange->line_term= $3; @@ -9306,372 +9778,426 @@ line_term: { DBUG_ASSERT(Lex->exchange != 0); Lex->exchange->line_start= $3; - }; + } + ; opt_ignore_lines: - /* empty */ + /* empty */ | IGNORE_SYM NUM LINES { DBUG_ASSERT(Lex->exchange != 0); Lex->exchange->skip_lines= atol($2.str); - }; + } + ; opt_field_or_var_spec: - /* empty */ { } - | '(' fields_or_vars ')' { } - | '(' ')' { }; + /* empty */ {} + | '(' fields_or_vars ')' {} + | '(' ')' {} + ; fields_or_vars: - fields_or_vars ',' field_or_var + fields_or_vars ',' field_or_var { Lex->field_list.push_back($3); } | field_or_var { Lex->field_list.push_back($1); } ; field_or_var: - simple_ident_nospvar {$$= $1;} + simple_ident_nospvar {$$= $1;} | '@' ident_or_text { $$= new Item_user_var_as_out_param($2); } ; opt_load_data_set_spec: - /* empty */ { } - | SET insert_update_list { }; - + /* empty */ {} + | SET insert_update_list {} + ; /* Common definitions */ text_literal: - TEXT_STRING_literal - { - THD *thd= YYTHD; - $$ = new Item_string($1.str,$1.length,thd->variables.collation_connection); - } - | NCHAR_STRING - { $$= new Item_string($1.str,$1.length,national_charset_info); } - | UNDERSCORE_CHARSET TEXT_STRING - { - $$ = new Item_string($2.str, $2.length, $1); - } - | text_literal TEXT_STRING_literal - { ((Item_string*) $1)->append($2.str,$2.length); } - ; + TEXT_STRING + { + LEX_STRING tmp; + THD *thd= YYTHD; + CHARSET_INFO *cs_con= thd->variables.collation_connection; + CHARSET_INFO *cs_cli= thd->variables.character_set_client; + uint repertoire= thd->lex->text_string_is_7bit && + my_charset_is_ascii_based(cs_cli) ? + MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30; + if (thd->charset_is_collation_connection || + (repertoire == MY_REPERTOIRE_ASCII && + my_charset_is_ascii_based(cs_con))) + tmp= $1; + else + thd->convert_string(&tmp, cs_con, $1.str, $1.length, cs_cli); + $$= new Item_string(tmp.str, tmp.length, cs_con, + DERIVATION_COERCIBLE, repertoire); + } + | NCHAR_STRING + { + uint repertoire= Lex->text_string_is_7bit ? + MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30; + DBUG_ASSERT(my_charset_is_ascii_based(national_charset_info)); + $$= new Item_string($1.str, $1.length, national_charset_info, + DERIVATION_COERCIBLE, repertoire); + } + | UNDERSCORE_CHARSET TEXT_STRING + { + $$= new Item_string($2.str, $2.length, $1); + ((Item_string*) $$)->set_repertoire_from_value(); + } + | text_literal TEXT_STRING_literal + { + Item_string* item= (Item_string*) $1; + item->append($2.str, $2.length); + if (!(item->collation.repertoire & MY_REPERTOIRE_EXTENDED)) + { + /* + If the string has been pure ASCII so far, + check the new part. + */ + CHARSET_INFO *cs= YYTHD->variables.collation_connection; + item->collation.repertoire|= my_string_repertoire(cs, + $2.str, + $2.length); + } + } + ; text_string: - TEXT_STRING_literal - { $$= new (YYTHD->mem_root) String($1.str,$1.length,YYTHD->variables.collation_connection); } - | HEX_NUM - { - Item *tmp= new Item_hex_string($1.str, $1.length); - /* - it is OK only emulate fix_fields, because we need only + TEXT_STRING_literal + { + $$= new (YYTHD->mem_root) String($1.str, + $1.length, + YYTHD->variables.collation_connection); + } + | HEX_NUM + { + Item *tmp= new Item_hex_string($1.str, $1.length); + /* + it is OK only emulate fix_fields, because we need only value of constant - */ - $$= tmp ? - tmp->quick_fix_field(), tmp->val_str((String*) 0) : - (String*) 0; - } + */ + $$= tmp ? + tmp->quick_fix_field(), tmp->val_str((String*) 0) : + (String*) 0; + } | BIN_NUM { - Item *tmp= new Item_bin_string($1.str, $1.length); - /* - it is OK only emulate fix_fields, because we need only + Item *tmp= new Item_bin_string($1.str, $1.length); + /* + it is OK only emulate fix_fields, because we need only value of constant - */ - $$= tmp ? tmp->quick_fix_field(), tmp->val_str((String*) 0) : - (String*) 0; + */ + $$= tmp ? tmp->quick_fix_field(), tmp->val_str((String*) 0) : + (String*) 0; } - ; + ; param_marker: - PARAM_MARKER - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - Lex_input_stream *lip= thd->m_lip; - Item_param *item; - if (! lex->parsing_options.allows_variable) - { - my_error(ER_VIEW_SELECT_VARIABLE, MYF(0)); - MYSQL_YYABORT; - } - item= new Item_param((uint) (lip->get_tok_start() - thd->query)); - if (!($$= item) || lex->param_list.push_back(item)) + PARAM_MARKER { - my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); - MYSQL_YYABORT; + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; + Item_param *item; + if (! lex->parsing_options.allows_variable) + { + my_error(ER_VIEW_SELECT_VARIABLE, MYF(0)); + MYSQL_YYABORT; + } + item= new Item_param((uint) (lip->get_tok_start() - thd->query)); + if (!($$= item) || lex->param_list.push_back(item)) + { + my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); + MYSQL_YYABORT; + } } - } - ; + ; signed_literal: - literal { $$ = $1; } - | '+' NUM_literal { $$ = $2; } - | '-' NUM_literal - { - $2->max_length++; - $$= $2->neg(); - } - ; - + literal { $$ = $1; } + | '+' NUM_literal { $$ = $2; } + | '-' NUM_literal + { + $2->max_length++; + $$= $2->neg(); + } + ; literal: - text_literal { $$ = $1; } - | NUM_literal { $$ = $1; } - | NULL_SYM + text_literal { $$ = $1; } + | NUM_literal { $$ = $1; } + | NULL_SYM { $$ = new Item_null(); YYTHD->m_lip->next_state=MY_LEX_OPERATOR_OR_IDENT; } - | FALSE_SYM { $$= new Item_int((char*) "FALSE",0,1); } - | TRUE_SYM { $$= new Item_int((char*) "TRUE",1,1); } - | HEX_NUM { $$ = new Item_hex_string($1.str, $1.length);} - | BIN_NUM { $$= new Item_bin_string($1.str, $1.length); } - | UNDERSCORE_CHARSET HEX_NUM - { - Item *tmp= new Item_hex_string($2.str, $2.length); - /* - it is OK only emulate fix_fieds, because we need only + | FALSE_SYM { $$= new Item_int((char*) "FALSE",0,1); } + | TRUE_SYM { $$= new Item_int((char*) "TRUE",1,1); } + | HEX_NUM { $$ = new Item_hex_string($1.str, $1.length);} + | BIN_NUM { $$= new Item_bin_string($1.str, $1.length); } + | UNDERSCORE_CHARSET HEX_NUM + { + Item *tmp= new Item_hex_string($2.str, $2.length); + /* + it is OK only emulate fix_fieds, because we need only value of constant - */ - String *str= tmp ? - tmp->quick_fix_field(), tmp->val_str((String*) 0) : - (String*) 0; - $$= new Item_string(str ? str->ptr() : "", - str ? str->length() : 0, - $1); - } - | UNDERSCORE_CHARSET BIN_NUM - { - Item *tmp= new Item_bin_string($2.str, $2.length); - /* - it is OK only emulate fix_fieds, because we need only + */ + String *str= tmp ? + tmp->quick_fix_field(), tmp->val_str((String*) 0) : + (String*) 0; + $$= new Item_string(str ? str->ptr() : "", + str ? str->length() : 0, + $1); + if ($$) + ((Item_string *) $$)->set_repertoire_from_value(); + } + | UNDERSCORE_CHARSET BIN_NUM + { + Item *tmp= new Item_bin_string($2.str, $2.length); + /* + it is OK only emulate fix_fieds, because we need only value of constant - */ - String *str= tmp ? - tmp->quick_fix_field(), tmp->val_str((String*) 0) : - (String*) 0; - $$= new Item_string(str ? str->ptr() : "", - str ? str->length() : 0, - Lex->charset); - } - | DATE_SYM text_literal { $$ = $2; } - | TIME_SYM text_literal { $$ = $2; } - | TIMESTAMP text_literal { $$ = $2; }; + */ + String *str= tmp ? + tmp->quick_fix_field(), tmp->val_str((String*) 0) : + (String*) 0; + $$= new Item_string(str ? str->ptr() : "", + str ? str->length() : 0, + Lex->charset); + } + | DATE_SYM text_literal { $$ = $2; } + | TIME_SYM text_literal { $$ = $2; } + | TIMESTAMP text_literal { $$ = $2; } + ; NUM_literal: - NUM { int error; $$ = new Item_int($1.str, (longlong) my_strtoll10($1.str, NULL, &error), $1.length); } - | LONG_NUM { int error; $$ = new Item_int($1.str, (longlong) my_strtoll10($1.str, NULL, &error), $1.length); } - | ULONGLONG_NUM { $$ = new Item_uint($1.str, $1.length); } + NUM + { + int error; + $$ = new Item_int($1.str, (longlong) my_strtoll10($1.str, NULL, &error), $1.length); + } + | LONG_NUM + { + int error; + $$ = new Item_int($1.str, (longlong) my_strtoll10($1.str, NULL, &error), $1.length); + } + | ULONGLONG_NUM + { $$ = new Item_uint($1.str, $1.length); } | DECIMAL_NUM - { - $$= new Item_decimal($1.str, $1.length, YYTHD->charset()); - if (YYTHD->net.report_error) - { - MYSQL_YYABORT; - } - } - | FLOAT_NUM - { - $$ = new Item_float($1.str, $1.length); - if (YYTHD->net.report_error) - { - MYSQL_YYABORT; - } - } - ; + { + $$= new Item_decimal($1.str, $1.length, YYTHD->charset()); + if (YYTHD->net.report_error) + { + MYSQL_YYABORT; + } + } + | FLOAT_NUM + { + $$ = new Item_float($1.str, $1.length); + if (YYTHD->net.report_error) + { + MYSQL_YYABORT; + } + } + ; /********************************************************************** ** Creating different items. **********************************************************************/ insert_ident: - simple_ident_nospvar { $$=$1; } - | table_wild { $$=$1; }; + simple_ident_nospvar { $$=$1; } + | table_wild { $$=$1; } + ; table_wild: - ident '.' '*' - { - SELECT_LEX *sel= Select; - $$ = new Item_field(Lex->current_context(), NullS, $1.str, "*"); - sel->with_wild++; - } - | ident '.' ident '.' '*' - { - SELECT_LEX *sel= Select; - $$ = new Item_field(Lex->current_context(), (YYTHD->client_capabilities & - CLIENT_NO_SCHEMA ? NullS : $1.str), - $3.str,"*"); - sel->with_wild++; - } - ; + ident '.' '*' + { + SELECT_LEX *sel= Select; + $$ = new Item_field(Lex->current_context(), NullS, $1.str, "*"); + sel->with_wild++; + } + | ident '.' ident '.' '*' + { + SELECT_LEX *sel= Select; + $$ = new Item_field(Lex->current_context(), (YYTHD->client_capabilities & + CLIENT_NO_SCHEMA ? NullS : $1.str), + $3.str,"*"); + sel->with_wild++; + } + ; order_ident: - expr { $$=$1; }; + expr { $$=$1; } + ; simple_ident: - ident - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - Lex_input_stream *lip= thd->m_lip; - sp_variable_t *spv; - sp_pcontext *spc = lex->spcont; - if (spc && (spv = spc->find_variable(&$1))) - { - /* We're compiling a stored procedure and found a variable */ - if (! lex->parsing_options.allows_variable) + ident + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; + sp_variable_t *spv; + sp_pcontext *spc = lex->spcont; + if (spc && (spv = spc->find_variable(&$1))) { - my_error(ER_VIEW_SELECT_VARIABLE, MYF(0)); - MYSQL_YYABORT; - } + /* 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, - lip->get_tok_start_prev() - - lex->sphead->m_tmp_query); + Item_splocal *splocal; + splocal= new Item_splocal($1, spv->offset, spv->type, + lip->get_tok_start_prev() - + lex->sphead->m_tmp_query, + lip->get_tok_end() - lip->get_tok_start_prev()); #ifndef DBUG_OFF - if (splocal) - splocal->m_sp= lex->sphead; + if (splocal) + splocal->m_sp= lex->sphead; #endif - $$ = (Item*) splocal; - lex->safe_to_cache_query=0; - } - else - { - SELECT_LEX *sel=Select; - $$= (sel->parsing_place != IN_HAVING || - sel->get_in_sum_expr() > 0) ? - (Item*) new Item_field(Lex->current_context(), NullS, NullS, $1.str) : - (Item*) new Item_ref(Lex->current_context(), NullS, NullS, $1.str); - } - } + $$ = (Item*) splocal; + lex->safe_to_cache_query=0; + } + else + { + SELECT_LEX *sel=Select; + $$= (sel->parsing_place != IN_HAVING || + sel->get_in_sum_expr() > 0) ? + (Item*) new Item_field(Lex->current_context(), NullS, NullS, $1.str) : + (Item*) new Item_ref(Lex->current_context(), NullS, NullS, $1.str); + } + } | simple_ident_q { $$= $1; } - ; + ; simple_ident_nospvar: - ident - { - SELECT_LEX *sel=Select; - $$= (sel->parsing_place != IN_HAVING || - sel->get_in_sum_expr() > 0) ? - (Item*) new Item_field(Lex->current_context(), NullS, NullS, $1.str) : - (Item*) new Item_ref(Lex->current_context(), NullS, NullS, $1.str); - } - | simple_ident_q { $$= $1; } - ; + ident + { + SELECT_LEX *sel=Select; + $$= (sel->parsing_place != IN_HAVING || + sel->get_in_sum_expr() > 0) ? + (Item*) new Item_field(Lex->current_context(), NullS, NullS, $1.str) : + (Item*) new Item_ref(Lex->current_context(), NullS, NullS, $1.str); + } + | simple_ident_q { $$= $1; } + ; simple_ident_q: - ident '.' ident - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - - /* - FIXME This will work ok in simple_ident_nospvar case because - we can't meet simple_ident_nospvar in trigger now. But it - should be changed in future. - */ - if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER && - (!my_strcasecmp(system_charset_info, $1.str, "NEW") || - !my_strcasecmp(system_charset_info, $1.str, "OLD"))) + ident '.' ident { - Item_trigger_field *trg_fld; - bool new_row= ($1.str[0]=='N' || $1.str[0]=='n'); + THD *thd= YYTHD; + LEX *lex= thd->lex; - if (lex->trg_chistics.event == TRG_EVENT_INSERT && - !new_row) + /* + FIXME This will work ok in simple_ident_nospvar case because + we can't meet simple_ident_nospvar in trigger now. But it + should be changed in future. + */ + if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER && + (!my_strcasecmp(system_charset_info, $1.str, "NEW") || + !my_strcasecmp(system_charset_info, $1.str, "OLD"))) { - my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "OLD", "on INSERT"); - MYSQL_YYABORT; - } + Item_trigger_field *trg_fld; + bool new_row= ($1.str[0]=='N' || $1.str[0]=='n'); - if (lex->trg_chistics.event == TRG_EVENT_DELETE && - new_row) - { - my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE"); - MYSQL_YYABORT; - } + if (lex->trg_chistics.event == TRG_EVENT_INSERT && + !new_row) + { + my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "OLD", "on INSERT"); + MYSQL_YYABORT; + } - DBUG_ASSERT(!new_row || - (lex->trg_chistics.event == TRG_EVENT_INSERT || - lex->trg_chistics.event == TRG_EVENT_UPDATE)); - const bool read_only= - !(new_row && lex->trg_chistics.action_time == TRG_ACTION_BEFORE); - if (!(trg_fld= new Item_trigger_field(Lex->current_context(), - new_row ? - Item_trigger_field::NEW_ROW: - Item_trigger_field::OLD_ROW, - $3.str, - SELECT_ACL, - read_only))) - 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"); + MYSQL_YYABORT; + } - /* - Let us add this item to list of all Item_trigger_field objects - in trigger. - */ - lex->trg_table_fields.link_in_list((uchar*) trg_fld, - (uchar**) &trg_fld-> - next_trg_field); - - $$= (Item *)trg_fld; - } - else - { - SELECT_LEX *sel= lex->current_select; - if (sel->no_table_names_allowed) - { - my_error(ER_TABLENAME_NOT_ALLOWED_HERE, - MYF(0), $1.str, thd->where); - } - $$= (sel->parsing_place != IN_HAVING || - sel->get_in_sum_expr() > 0) ? - (Item*) new Item_field(Lex->current_context(), NullS, $1.str, $3.str) : - (Item*) new Item_ref(Lex->current_context(), NullS, $1.str, $3.str); - } - } - | '.' ident '.' ident - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - SELECT_LEX *sel= lex->current_select; - if (sel->no_table_names_allowed) - { - my_error(ER_TABLENAME_NOT_ALLOWED_HERE, - MYF(0), $2.str, thd->where); - } - $$= (sel->parsing_place != IN_HAVING || - sel->get_in_sum_expr() > 0) ? - (Item*) new Item_field(Lex->current_context(), NullS, $2.str, $4.str) : - (Item*) new Item_ref(Lex->current_context(), NullS, $2.str, $4.str); - } - | ident '.' ident '.' ident - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - SELECT_LEX *sel= lex->current_select; - if (sel->no_table_names_allowed) - { - my_error(ER_TABLENAME_NOT_ALLOWED_HERE, - MYF(0), $3.str, thd->where); - } - $$= (sel->parsing_place != IN_HAVING || - sel->get_in_sum_expr() > 0) ? - (Item*) new Item_field(Lex->current_context(), - (YYTHD->client_capabilities & - CLIENT_NO_SCHEMA ? NullS : $1.str), - $3.str, $5.str) : - (Item*) new Item_ref(Lex->current_context(), - (YYTHD->client_capabilities & - CLIENT_NO_SCHEMA ? NullS : $1.str), - $3.str, $5.str); - }; + DBUG_ASSERT(!new_row || + (lex->trg_chistics.event == TRG_EVENT_INSERT || + lex->trg_chistics.event == TRG_EVENT_UPDATE)); + const bool read_only= + !(new_row && lex->trg_chistics.action_time == TRG_ACTION_BEFORE); + if (!(trg_fld= new Item_trigger_field(Lex->current_context(), + new_row ? + Item_trigger_field::NEW_ROW: + Item_trigger_field::OLD_ROW, + $3.str, + SELECT_ACL, + read_only))) + MYSQL_YYABORT; + /* + Let us add this item to list of all Item_trigger_field objects + in trigger. + */ + lex->trg_table_fields.link_in_list((uchar*) trg_fld, + (uchar**) &trg_fld->next_trg_field); + + $$= (Item *)trg_fld; + } + else + { + SELECT_LEX *sel= lex->current_select; + if (sel->no_table_names_allowed) + { + my_error(ER_TABLENAME_NOT_ALLOWED_HERE, + MYF(0), $1.str, thd->where); + } + $$= (sel->parsing_place != IN_HAVING || + sel->get_in_sum_expr() > 0) ? + (Item*) new Item_field(Lex->current_context(), NullS, $1.str, $3.str) : + (Item*) new Item_ref(Lex->current_context(), NullS, $1.str, $3.str); + } + } + | '.' ident '.' ident + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + SELECT_LEX *sel= lex->current_select; + if (sel->no_table_names_allowed) + { + my_error(ER_TABLENAME_NOT_ALLOWED_HERE, + MYF(0), $2.str, thd->where); + } + $$= (sel->parsing_place != IN_HAVING || + sel->get_in_sum_expr() > 0) ? + (Item*) new Item_field(Lex->current_context(), NullS, $2.str, $4.str) : + (Item*) new Item_ref(Lex->current_context(), NullS, $2.str, $4.str); + } + | ident '.' ident '.' ident + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + SELECT_LEX *sel= lex->current_select; + if (sel->no_table_names_allowed) + { + my_error(ER_TABLENAME_NOT_ALLOWED_HERE, + MYF(0), $3.str, thd->where); + } + $$= (sel->parsing_place != IN_HAVING || + sel->get_in_sum_expr() > 0) ? + (Item*) new Item_field(Lex->current_context(), + (YYTHD->client_capabilities & + CLIENT_NO_SCHEMA ? NullS : $1.str), + $3.str, $5.str) : + (Item*) new Item_ref(Lex->current_context(), + (YYTHD->client_capabilities & + CLIENT_NO_SCHEMA ? NullS : $1.str), + $3.str, $5.str); + } + ; field_ident: - ident { $$=$1;} - | ident '.' ident '.' ident + ident { $$=$1;} + | ident '.' ident '.' ident { TABLE_LIST *table= (TABLE_LIST*) Select->table_list.first; if (my_strcasecmp(table_alias_charset, $1.str, table->db)) @@ -9687,7 +10213,7 @@ field_ident: } $$=$5; } - | ident '.' ident + | ident '.' ident { TABLE_LIST *table= (TABLE_LIST*) Select->table_list.first; if (my_strcasecmp(table_alias_charset, $1.str, table->alias)) @@ -9697,25 +10223,30 @@ field_ident: } $$=$3; } - | '.' ident { $$=$2;} /* For Delphi */; + | '.' ident { $$=$2;} /* For Delphi */ + ; table_ident: - ident { $$=new Table_ident($1); } - | ident '.' ident { $$=new Table_ident(YYTHD, $1,$3,0);} - | '.' ident { $$=new Table_ident($2);} /* For Delphi */ + ident { $$=new Table_ident($1); } + | ident '.' ident { $$=new Table_ident(YYTHD, $1,$3,0);} + | '.' ident { $$=new Table_ident($2);} /* For Delphi */ ; table_ident_nodb: - ident { LEX_STRING db={(char*) any_db,3}; $$=new Table_ident(YYTHD, db,$1,0); } + ident + { + LEX_STRING db={(char*) any_db,3}; + $$=new Table_ident(YYTHD, db,$1,0); + } ; IDENT_sys: - IDENT { $$= $1; } - | IDENT_QUOTED - { - THD *thd= YYTHD; + IDENT { $$= $1; } + | IDENT_QUOTED + { + THD *thd= YYTHD; - if (thd->charset_is_system_charset) + if (thd->charset_is_system_charset) { CHARSET_INFO *cs= system_charset_info; int dummy_error; @@ -9728,179 +10259,180 @@ IDENT_sys: cs->csname, $1.str + wlen); MYSQL_YYABORT; } - $$= $1; + $$= $1; } - else - thd->convert_string(&$$, system_charset_info, - $1.str, $1.length, thd->charset()); - } - ; + else + thd->convert_string(&$$, system_charset_info, + $1.str, $1.length, thd->charset()); + } + ; TEXT_STRING_sys: - TEXT_STRING - { - THD *thd= YYTHD; + TEXT_STRING + { + THD *thd= YYTHD; - if (thd->charset_is_system_charset) - $$= $1; - else - thd->convert_string(&$$, system_charset_info, - $1.str, $1.length, thd->charset()); - } - ; + if (thd->charset_is_system_charset) + $$= $1; + else + thd->convert_string(&$$, system_charset_info, + $1.str, $1.length, thd->charset()); + } + ; TEXT_STRING_literal: - TEXT_STRING - { - THD *thd= YYTHD; - - if (thd->charset_is_collation_connection) - $$= $1; - else - thd->convert_string(&$$, thd->variables.collation_connection, - $1.str, $1.length, thd->charset()); - } - ; + TEXT_STRING + { + THD *thd= YYTHD; + if (thd->charset_is_collation_connection) + $$= $1; + else + thd->convert_string(&$$, thd->variables.collation_connection, + $1.str, $1.length, thd->charset()); + } + ; TEXT_STRING_filesystem: - TEXT_STRING - { - THD *thd= YYTHD; + TEXT_STRING + { + THD *thd= YYTHD; - if (thd->charset_is_character_set_filesystem) - $$= $1; - else - thd->convert_string(&$$, thd->variables.character_set_filesystem, - $1.str, $1.length, thd->charset()); - } - ; + if (thd->charset_is_character_set_filesystem) + $$= $1; + else + thd->convert_string(&$$, thd->variables.character_set_filesystem, + $1.str, $1.length, thd->charset()); + } + ; ident: - IDENT_sys { $$=$1; } - | READ_ONLY_SYM - { - THD *thd= YYTHD; - $$.str= thd->strmake("read_only",9); - $$.length= 9; - } - | keyword - { - THD *thd= YYTHD; - $$.str= thd->strmake($1.str, $1.length); - $$.length= $1.length; - } - ; + IDENT_sys { $$=$1; } + | READ_ONLY_SYM + { + THD *thd= YYTHD; + $$.str= thd->strmake("read_only",9); + $$.length= 9; + } + | keyword + { + THD *thd= YYTHD; + $$.str= thd->strmake($1.str, $1.length); + $$.length= $1.length; + } + ; label_ident: - IDENT_sys { $$=$1; } - | keyword_sp - { - THD *thd= YYTHD; - $$.str= thd->strmake($1.str, $1.length); - $$.length= $1.length; - } - ; + IDENT_sys { $$=$1; } + | keyword_sp + { + THD *thd= YYTHD; + $$.str= thd->strmake($1.str, $1.length); + $$.length= $1.length; + } + ; ident_or_text: - ident { $$=$1;} - | TEXT_STRING_sys { $$=$1;} - | LEX_HOSTNAME { $$=$1;}; + ident { $$=$1;} + | TEXT_STRING_sys { $$=$1;} + | LEX_HOSTNAME { $$=$1;} + ; user: - ident_or_text - { - THD *thd= YYTHD; - if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) - MYSQL_YYABORT; - $$->user = $1; - $$->host.str= (char *) "%"; - $$->host.length= 1; + ident_or_text + { + THD *thd= YYTHD; + if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + MYSQL_YYABORT; + $$->user = $1; + $$->host.str= (char *) "%"; + $$->host.length= 1; - if (check_string_char_length(&$$->user, ER(ER_USERNAME), - USERNAME_CHAR_LENGTH, - system_charset_info, 0)) - MYSQL_YYABORT; - } - | ident_or_text '@' ident_or_text - { - THD *thd= YYTHD; - if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) - MYSQL_YYABORT; - $$->user = $1; $$->host=$3; - - if (check_string_char_length(&$$->user, ER(ER_USERNAME), + if (check_string_char_length(&$$->user, ER(ER_USERNAME), + USERNAME_CHAR_LENGTH, + system_charset_info, 0)) + MYSQL_YYABORT; + } + | ident_or_text '@' ident_or_text + { + THD *thd= YYTHD; + if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + MYSQL_YYABORT; + $$->user = $1; $$->host=$3; + + if (check_string_char_length(&$$->user, ER(ER_USERNAME), USERNAME_CHAR_LENGTH, system_charset_info, 0) || - check_string_byte_length(&$$->host, ER(ER_HOSTNAME), + check_string_byte_length(&$$->host, ER(ER_HOSTNAME), HOSTNAME_LENGTH)) - MYSQL_YYABORT; - } - | CURRENT_USER optional_braces - { - if (!($$=(LEX_USER*) YYTHD->alloc(sizeof(st_lex_user)))) - MYSQL_YYABORT; - /* - empty LEX_USER means current_user and - will be handled in the get_current_user() function - later - */ - bzero($$, sizeof(LEX_USER)); - }; + MYSQL_YYABORT; + } + | CURRENT_USER optional_braces + { + if (!($$=(LEX_USER*) YYTHD->alloc(sizeof(st_lex_user)))) + MYSQL_YYABORT; + /* + empty LEX_USER means current_user and + will be handled in the get_current_user() function + later + */ + bzero($$, sizeof(LEX_USER)); + } + ; /* Keyword that we allow for identifiers (except SP labels) */ keyword: - keyword_sp {} - | ASCII_SYM {} - | BACKUP_SYM {} - | BEGIN_SYM {} - | BYTE_SYM {} - | CACHE_SYM {} - | CHARSET {} - | CHECKSUM_SYM {} - | CLOSE_SYM {} - | COMMENT_SYM {} - | COMMIT_SYM {} - | CONTAINS_SYM {} + keyword_sp {} + | ASCII_SYM {} + | BACKUP_SYM {} + | BEGIN_SYM {} + | BYTE_SYM {} + | CACHE_SYM {} + | CHARSET {} + | CHECKSUM_SYM {} + | CLOSE_SYM {} + | COMMENT_SYM {} + | COMMIT_SYM {} + | CONTAINS_SYM {} | DEALLOCATE_SYM {} - | DO_SYM {} - | END {} - | EXECUTE_SYM {} - | FLUSH_SYM {} - | HANDLER_SYM {} - | HELP_SYM {} - | HOST_SYM {} + | DO_SYM {} + | END {} + | EXECUTE_SYM {} + | FLUSH_SYM {} + | HANDLER_SYM {} + | HELP_SYM {} + | HOST_SYM {} | INSTALL_SYM {} - | LANGUAGE_SYM {} - | NO_SYM {} - | OPEN_SYM {} - | OPTIONS_SYM {} - | OWNER_SYM {} + | LANGUAGE_SYM {} + | NO_SYM {} + | OPEN_SYM {} + | OPTIONS_SYM {} + | OWNER_SYM {} | PARSER_SYM {} - | PARTITION_SYM {} + | PARTITION_SYM {} | PORT_SYM {} | PREPARE_SYM {} - | REMOVE_SYM {} - | REPAIR {} - | RESET_SYM {} - | RESTORE_SYM {} - | ROLLBACK_SYM {} - | SAVEPOINT_SYM {} - | SECURITY_SYM {} + | REMOVE_SYM {} + | REPAIR {} + | RESET_SYM {} + | RESTORE_SYM {} + | ROLLBACK_SYM {} + | SAVEPOINT_SYM {} + | SECURITY_SYM {} | SERVER_SYM {} - | SIGNED_SYM {} - | SOCKET_SYM {} - | SLAVE {} + | SIGNED_SYM {} + | SOCKET_SYM {} + | SLAVE {} | SONAME_SYM {} - | START_SYM {} - | STOP_SYM {} - | TRUNCATE_SYM {} - | UNICODE_SYM {} + | START_SYM {} + | STOP_SYM {} + | TRUNCATE_SYM {} + | UNICODE_SYM {} | UNINSTALL_SYM {} - | WRAPPER_SYM {} + | WRAPPER_SYM {} | XA_SYM {} | UPGRADE_SYM {} - ; + ; /* * Keywords that we allow for labels in SPs. @@ -9909,590 +10441,594 @@ keyword: * conflicts. */ keyword_sp: - ACTION {} - | ADDDATE_SYM {} - | AFTER_SYM {} - | AGAINST {} - | AGGREGATE_SYM {} - | ALGORITHM_SYM {} - | ANY_SYM {} - | AT_SYM {} - | AUTHORS_SYM {} - | AUTO_INC {} - | AUTOEXTEND_SIZE_SYM {} - | AVG_ROW_LENGTH {} - | AVG_SYM {} - | BINLOG_SYM {} - | BIT_SYM {} - | BOOL_SYM {} - | BOOLEAN_SYM {} - | BTREE_SYM {} - | CASCADED {} - | CHAIN_SYM {} - | CHANGED {} - | CIPHER_SYM {} - | CLIENT_SYM {} - | COALESCE {} - | CODE_SYM {} - | COLLATION_SYM {} - | COLUMNS {} - | COMMITTED_SYM {} - | COMPACT_SYM {} - | COMPLETION_SYM {} - | COMPRESSED_SYM {} - | CONCURRENT {} - | CONNECTION_SYM {} - | CONSISTENT_SYM {} - | CONTRIBUTORS_SYM {} - | CUBE_SYM {} - | DATA_SYM {} - | DATAFILE_SYM {} - | DATETIME {} - | DATE_SYM {} - | DAY_SYM {} - | DEFINER_SYM {} - | DELAY_KEY_WRITE_SYM {} - | DES_KEY_FILE {} - | DIRECTORY_SYM {} - | DISABLE_SYM {} - | DISCARD {} - | DISK_SYM {} - | DUMPFILE {} - | DUPLICATE_SYM {} - | DYNAMIC_SYM {} - | ENDS_SYM {} - | ENUM {} - | ENGINE_SYM {} - | ENGINES_SYM {} - | ERRORS {} - | ESCAPE_SYM {} - | EVENT_SYM {} - | EVENTS_SYM {} - | EVERY_SYM {} - | EXPANSION_SYM {} - | EXTENDED_SYM {} - | EXTENT_SIZE_SYM {} - | FAST_SYM {} - | FOUND_SYM {} - | ENABLE_SYM {} - | FULL {} - | FILE_SYM {} - | FIRST_SYM {} - | FIXED_SYM {} - | FRAC_SECOND_SYM {} - | GEOMETRY_SYM {} - | GEOMETRYCOLLECTION {} - | GET_FORMAT {} - | GRANTS {} - | GLOBAL_SYM {} - | HASH_SYM {} - | HOSTS_SYM {} - | HOUR_SYM {} - | IDENTIFIED_SYM {} - | INVOKER_SYM {} - | IMPORT {} - | INDEXES {} - | INITIAL_SIZE_SYM {} - | ISOLATION {} - | ISSUER_SYM {} - | INNOBASE_SYM {} - | INSERT_METHOD {} - | KEY_BLOCK_SIZE {} - | LAST_SYM {} - | LEAVES {} - | LESS_SYM {} - | LEVEL_SYM {} - | LINESTRING {} - | LIST_SYM {} - | LOCAL_SYM {} - | LOCKS_SYM {} - | LOGFILE_SYM {} - | LOGS_SYM {} - | MAX_ROWS {} - | MASTER_SYM {} - | MASTER_HOST_SYM {} - | MASTER_PORT_SYM {} - | MASTER_LOG_FILE_SYM {} - | MASTER_LOG_POS_SYM {} - | MASTER_USER_SYM {} - | MASTER_PASSWORD_SYM {} - | MASTER_SERVER_ID_SYM {} - | MASTER_CONNECT_RETRY_SYM {} - | MASTER_SSL_SYM {} - | MASTER_SSL_CA_SYM {} - | MASTER_SSL_CAPATH_SYM {} - | MASTER_SSL_CERT_SYM {} - | MASTER_SSL_CIPHER_SYM {} - | MASTER_SSL_KEY_SYM {} - | MAX_CONNECTIONS_PER_HOUR {} - | MAX_QUERIES_PER_HOUR {} - | MAX_SIZE_SYM {} - | MAX_UPDATES_PER_HOUR {} - | MAX_USER_CONNECTIONS_SYM {} - | MAX_VALUE_SYM {} - | MEDIUM_SYM {} - | MEMORY_SYM {} - | MERGE_SYM {} - | MICROSECOND_SYM {} - | MIGRATE_SYM {} - | MINUTE_SYM {} - | MIN_ROWS {} - | MODIFY_SYM {} - | MODE_SYM {} - | MONTH_SYM {} - | MULTILINESTRING {} - | MULTIPOINT {} - | MULTIPOLYGON {} - | MUTEX_SYM {} - | NAME_SYM {} - | NAMES_SYM {} - | NATIONAL_SYM {} - | NCHAR_SYM {} - | NDBCLUSTER_SYM {} - | NEXT_SYM {} - | NEW_SYM {} - | NO_WAIT_SYM {} - | NODEGROUP_SYM {} - | NONE_SYM {} - | NVARCHAR_SYM {} - | OFFSET_SYM {} - | OLD_PASSWORD {} - | ONE_SHOT_SYM {} - | ONE_SYM {} - | PACK_KEYS_SYM {} - | PAGE_SYM {} - | PAGE_CHECKSUM_SYM {} - | PARTIAL {} - | PARTITIONING_SYM {} - | PARTITIONS_SYM {} - | PASSWORD {} - | PHASE_SYM {} - | PLUGIN_SYM {} - | PLUGINS_SYM {} - | POINT_SYM {} - | POLYGON {} - | PRESERVE_SYM {} - | PREV_SYM {} - | PRIVILEGES {} - | PROCESS {} - | PROCESSLIST_SYM {} - | QUARTER_SYM {} - | QUERY_SYM {} - | QUICK {} - | REBUILD_SYM {} - | RECOVER_SYM {} - | REDO_BUFFER_SIZE_SYM {} - | REDOFILE_SYM {} - | REDUNDANT_SYM {} - | RELAY_LOG_FILE_SYM {} - | RELAY_LOG_POS_SYM {} - | RELAY_THREAD {} - | RELOAD {} - | REORGANIZE_SYM {} - | REPEATABLE_SYM {} - | REPLICATION {} - | RESOURCES {} - | RESUME_SYM {} - | RETURNS_SYM {} - | ROLLUP_SYM {} - | ROUTINE_SYM {} - | ROWS_SYM {} - | ROW_FORMAT_SYM {} - | ROW_SYM {} - | RTREE_SYM {} - | SCHEDULE_SYM {} - | SECOND_SYM {} - | SERIAL_SYM {} - | SERIALIZABLE_SYM {} - | SESSION_SYM {} - | SIMPLE_SYM {} - | SHARE_SYM {} - | SHUTDOWN {} - | SNAPSHOT_SYM {} - | SOUNDS_SYM {} - | SQL_CACHE_SYM {} - | SQL_BUFFER_RESULT {} - | SQL_NO_CACHE_SYM {} - | SQL_THREAD {} - | STARTS_SYM {} - | STATUS_SYM {} - | STORAGE_SYM {} - | STRING_SYM {} - | SUBDATE_SYM {} - | SUBJECT_SYM {} - | SUBPARTITION_SYM {} - | SUBPARTITIONS_SYM {} - | SUPER_SYM {} - | SUSPEND_SYM {} - | TABLE_CHECKSUM_SYM {} - | TABLES {} - | TABLESPACE {} - | TEMPORARY {} - | TEMPTABLE_SYM {} - | TEXT_SYM {} - | THAN_SYM {} - | TRANSACTION_SYM {} - | TRANSACTIONAL_SYM {} - | TRIGGERS_SYM {} - | TIMESTAMP {} - | TIMESTAMP_ADD {} - | TIMESTAMP_DIFF {} - | TIME_SYM {} - | TYPES_SYM {} - | TYPE_SYM {} - | UDF_RETURNS_SYM {} - | FUNCTION_SYM {} - | UNCOMMITTED_SYM {} - | UNDEFINED_SYM {} - | UNDO_BUFFER_SIZE_SYM {} - | UNDOFILE_SYM {} - | UNKNOWN_SYM {} - | UNTIL_SYM {} - | USER {} - | USE_FRM {} - | VARIABLES {} - | VIEW_SYM {} - | VALUE_SYM {} - | WARNINGS {} - | WAIT_SYM {} - | WEEK_SYM {} - | WORK_SYM {} - | X509_SYM {} - | YEAR_SYM {} - ; + ACTION {} + | ADDDATE_SYM {} + | AFTER_SYM {} + | AGAINST {} + | AGGREGATE_SYM {} + | ALGORITHM_SYM {} + | ANY_SYM {} + | AT_SYM {} + | AUTHORS_SYM {} + | AUTO_INC {} + | AUTOEXTEND_SIZE_SYM {} + | AVG_ROW_LENGTH {} + | AVG_SYM {} + | BINLOG_SYM {} + | BIT_SYM {} + | BOOL_SYM {} + | BOOLEAN_SYM {} + | BTREE_SYM {} + | CASCADED {} + | CHAIN_SYM {} + | CHANGED {} + | CIPHER_SYM {} + | CLIENT_SYM {} + | COALESCE {} + | CODE_SYM {} + | COLLATION_SYM {} + | COLUMNS {} + | COMMITTED_SYM {} + | COMPACT_SYM {} + | COMPLETION_SYM {} + | COMPRESSED_SYM {} + | CONCURRENT {} + | CONNECTION_SYM {} + | CONSISTENT_SYM {} + | CONTRIBUTORS_SYM {} + | CUBE_SYM {} + | DATA_SYM {} + | DATAFILE_SYM {} + | DATETIME {} + | DATE_SYM {} + | DAY_SYM {} + | DEFINER_SYM {} + | DELAY_KEY_WRITE_SYM {} + | DES_KEY_FILE {} + | DIRECTORY_SYM {} + | DISABLE_SYM {} + | DISCARD {} + | DISK_SYM {} + | DUMPFILE {} + | DUPLICATE_SYM {} + | DYNAMIC_SYM {} + | ENDS_SYM {} + | ENUM {} + | ENGINE_SYM {} + | ENGINES_SYM {} + | ERRORS {} + | ESCAPE_SYM {} + | EVENT_SYM {} + | EVENTS_SYM {} + | EVERY_SYM {} + | EXPANSION_SYM {} + | EXTENDED_SYM {} + | EXTENT_SIZE_SYM {} + | FAST_SYM {} + | FOUND_SYM {} + | ENABLE_SYM {} + | FULL {} + | FILE_SYM {} + | FIRST_SYM {} + | FIXED_SYM {} + | FRAC_SECOND_SYM {} + | GEOMETRY_SYM {} + | GEOMETRYCOLLECTION {} + | GET_FORMAT {} + | GRANTS {} + | GLOBAL_SYM {} + | HASH_SYM {} + | HOSTS_SYM {} + | HOUR_SYM {} + | IDENTIFIED_SYM {} + | INVOKER_SYM {} + | IMPORT {} + | INDEXES {} + | INITIAL_SIZE_SYM {} + | ISOLATION {} + | ISSUER_SYM {} + | INNOBASE_SYM {} + | INSERT_METHOD {} + | KEY_BLOCK_SIZE {} + | LAST_SYM {} + | LEAVES {} + | LESS_SYM {} + | LEVEL_SYM {} + | LINESTRING {} + | LIST_SYM {} + | LOCAL_SYM {} + | LOCKS_SYM {} + | LOGFILE_SYM {} + | LOGS_SYM {} + | MAX_ROWS {} + | MASTER_SYM {} + | MASTER_HOST_SYM {} + | MASTER_PORT_SYM {} + | MASTER_LOG_FILE_SYM {} + | MASTER_LOG_POS_SYM {} + | MASTER_USER_SYM {} + | MASTER_PASSWORD_SYM {} + | MASTER_SERVER_ID_SYM {} + | MASTER_CONNECT_RETRY_SYM {} + | MASTER_SSL_SYM {} + | MASTER_SSL_CA_SYM {} + | MASTER_SSL_CAPATH_SYM {} + | MASTER_SSL_CERT_SYM {} + | MASTER_SSL_CIPHER_SYM {} + | MASTER_SSL_KEY_SYM {} + | MAX_CONNECTIONS_PER_HOUR {} + | MAX_QUERIES_PER_HOUR {} + | MAX_SIZE_SYM {} + | MAX_UPDATES_PER_HOUR {} + | MAX_USER_CONNECTIONS_SYM {} + | MAX_VALUE_SYM {} + | MEDIUM_SYM {} + | MEMORY_SYM {} + | MERGE_SYM {} + | MICROSECOND_SYM {} + | MIGRATE_SYM {} + | MINUTE_SYM {} + | MIN_ROWS {} + | MODIFY_SYM {} + | MODE_SYM {} + | MONTH_SYM {} + | MULTILINESTRING {} + | MULTIPOINT {} + | MULTIPOLYGON {} + | MUTEX_SYM {} + | NAME_SYM {} + | NAMES_SYM {} + | NATIONAL_SYM {} + | NCHAR_SYM {} + | NDBCLUSTER_SYM {} + | NEXT_SYM {} + | NEW_SYM {} + | NO_WAIT_SYM {} + | NODEGROUP_SYM {} + | NONE_SYM {} + | NVARCHAR_SYM {} + | OFFSET_SYM {} + | OLD_PASSWORD {} + | ONE_SHOT_SYM {} + | ONE_SYM {} + | PACK_KEYS_SYM {} + | PAGE_SYM {} + | PAGE_CHECKSUM_SYM {} + | PARTIAL {} + | PARTITIONING_SYM {} + | PARTITIONS_SYM {} + | PASSWORD {} + | PHASE_SYM {} + | PLUGIN_SYM {} + | PLUGINS_SYM {} + | POINT_SYM {} + | POLYGON {} + | PRESERVE_SYM {} + | PREV_SYM {} + | PRIVILEGES {} + | PROCESS {} + | PROCESSLIST_SYM {} + | QUARTER_SYM {} + | QUERY_SYM {} + | QUICK {} + | REBUILD_SYM {} + | RECOVER_SYM {} + | REDO_BUFFER_SIZE_SYM {} + | REDOFILE_SYM {} + | REDUNDANT_SYM {} + | RELAY_LOG_FILE_SYM {} + | RELAY_LOG_POS_SYM {} + | RELAY_THREAD {} + | RELOAD {} + | REORGANIZE_SYM {} + | REPEATABLE_SYM {} + | REPLICATION {} + | RESOURCES {} + | RESUME_SYM {} + | RETURNS_SYM {} + | ROLLUP_SYM {} + | ROUTINE_SYM {} + | ROWS_SYM {} + | ROW_FORMAT_SYM {} + | ROW_SYM {} + | RTREE_SYM {} + | SCHEDULE_SYM {} + | SECOND_SYM {} + | SERIAL_SYM {} + | SERIALIZABLE_SYM {} + | SESSION_SYM {} + | SIMPLE_SYM {} + | SHARE_SYM {} + | SHUTDOWN {} + | SNAPSHOT_SYM {} + | SOUNDS_SYM {} + | SQL_CACHE_SYM {} + | SQL_BUFFER_RESULT {} + | SQL_NO_CACHE_SYM {} + | SQL_THREAD {} + | STARTS_SYM {} + | STATUS_SYM {} + | STORAGE_SYM {} + | STRING_SYM {} + | SUBDATE_SYM {} + | SUBJECT_SYM {} + | SUBPARTITION_SYM {} + | SUBPARTITIONS_SYM {} + | SUPER_SYM {} + | SUSPEND_SYM {} + | TABLES {} + | TABLE_CHECKSUM_SYM {} + | TABLESPACE {} + | TEMPORARY {} + | TEMPTABLE_SYM {} + | TEXT_SYM {} + | THAN_SYM {} + | TRANSACTION_SYM {} + | TRANSACTIONAL_SYM {} + | TRIGGERS_SYM {} + | TIMESTAMP {} + | TIMESTAMP_ADD {} + | TIMESTAMP_DIFF {} + | TIME_SYM {} + | TYPES_SYM {} + | TYPE_SYM {} + | UDF_RETURNS_SYM {} + | FUNCTION_SYM {} + | UNCOMMITTED_SYM {} + | UNDEFINED_SYM {} + | UNDO_BUFFER_SIZE_SYM {} + | UNDOFILE_SYM {} + | UNKNOWN_SYM {} + | UNTIL_SYM {} + | USER {} + | USE_FRM {} + | VARIABLES {} + | VIEW_SYM {} + | VALUE_SYM {} + | WARNINGS {} + | WAIT_SYM {} + | WEEK_SYM {} + | WORK_SYM {} + | X509_SYM {} + | YEAR_SYM {} + ; /* Option functions */ set: - SET opt_option - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_SET_OPTION; - mysql_init_select(lex); - lex->option_type=OPT_SESSION; - lex->var_list.empty(); - lex->one_shot_set= 0; - } - option_value_list - {} - ; + SET opt_option + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SET_OPTION; + mysql_init_select(lex); + lex->option_type=OPT_SESSION; + lex->var_list.empty(); + lex->one_shot_set= 0; + } + option_value_list + {} + ; opt_option: - /* empty */ {} - | OPTION {}; + /* empty */ {} + | OPTION {} + ; option_value_list: - option_type_value - | option_value_list ',' option_type_value; + option_type_value + | option_value_list ',' option_type_value + ; option_type_value: - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - Lex_input_stream *lip= thd->m_lip; - - if (lex->sphead) { - /* - If we are in SP we want have own LEX for each assignment. - This is mostly because it is hard for several sp_instr_set - and sp_instr_set_trigger instructions share one LEX. - (Well, it is theoretically possible but adds some extra - overhead on preparation for execution stage and IMO less - robust). - - QQ: May be we should simply prohibit group assignments in SP? - */ - lex->sphead->reset_lex(thd); - lex= thd->lex; - - /* Set new LEX as if we at start of set rule. */ - lex->sql_command= SQLCOM_SET_OPTION; - mysql_init_select(lex); - lex->option_type=OPT_SESSION; - lex->var_list.empty(); - lex->one_shot_set= 0; - lex->sphead->m_tmp_query= lip->get_tok_start(); - } - } - ext_option_value - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - Lex_input_stream *lip= thd->m_lip; - - if (lex->sphead) - { - sp_head *sp= lex->sphead; + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; - if (!lex->var_list.is_empty()) - { + if (lex->sphead) + { /* - We have assignment to user or system variable or - option setting, so we should construct sp_instr_stmt - for it. + If we are in SP we want have own LEX for each assignment. + This is mostly because it is hard for several sp_instr_set + and sp_instr_set_trigger instructions share one LEX. + (Well, it is theoretically possible but adds some extra + overhead on preparation for execution stage and IMO less + robust). + + QQ: May be we should simply prohibit group assignments in SP? */ - LEX_STRING qbuff; - sp_instr_stmt *i; + lex->sphead->reset_lex(thd); + lex= thd->lex; + + /* Set new LEX as if we at start of set rule. */ + lex->sql_command= SQLCOM_SET_OPTION; + mysql_init_select(lex); + lex->option_type=OPT_SESSION; + lex->var_list.empty(); + lex->one_shot_set= 0; + lex->sphead->m_tmp_query= lip->get_tok_start(); + } + } + ext_option_value + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; - if (!(i= new sp_instr_stmt(sp->instructions(), lex->spcont, - lex))) - MYSQL_YYABORT; + if (lex->sphead) + { + sp_head *sp= lex->sphead; - /* - Extract the query statement from the tokenizer. The - end is either lip->ptr, if there was no lookahead, - lip->tok_end otherwise. - */ - if (yychar == YYEMPTY) - qbuff.length= lip->get_ptr() - sp->m_tmp_query; - else - qbuff.length= lip->get_tok_end() - sp->m_tmp_query; + if (!lex->var_list.is_empty()) + { + /* + We have assignment to user or system variable or + option setting, so we should construct sp_instr_stmt + for it. + */ + LEX_STRING qbuff; + sp_instr_stmt *i; + + if (!(i= new sp_instr_stmt(sp->instructions(), lex->spcont, + lex))) + MYSQL_YYABORT; - if (!(qbuff.str= (char*) alloc_root(thd->mem_root, - qbuff.length + 5))) - MYSQL_YYABORT; + /* + Extract the query statement from the tokenizer. The + end is either lip->ptr, if there was no lookahead, + lip->tok_end otherwise. + */ + if (yychar == YYEMPTY) + qbuff.length= lip->get_ptr() - sp->m_tmp_query; + else + qbuff.length= lip->get_tok_end() - sp->m_tmp_query; + + if (!(qbuff.str= (char*) alloc_root(thd->mem_root, + qbuff.length + 5))) + MYSQL_YYABORT; - strmake(strmake(qbuff.str, "SET ", 4), sp->m_tmp_query, - qbuff.length); - qbuff.length+= 4; - i->m_query= qbuff; - sp->add_instr(i); + strmake(strmake(qbuff.str, "SET ", 4), sp->m_tmp_query, + qbuff.length); + qbuff.length+= 4; + i->m_query= qbuff; + sp->add_instr(i); + } + lex->sphead->restore_lex(thd); } - lex->sphead->restore_lex(thd); } - }; + ; option_type: - option_type2 {} - | GLOBAL_SYM { $$=OPT_GLOBAL; } - | LOCAL_SYM { $$=OPT_SESSION; } - | SESSION_SYM { $$=OPT_SESSION; } - ; + option_type2 {} + | GLOBAL_SYM { $$=OPT_GLOBAL; } + | LOCAL_SYM { $$=OPT_SESSION; } + | SESSION_SYM { $$=OPT_SESSION; } + ; option_type2: - /* empty */ { $$= OPT_DEFAULT; } - | ONE_SHOT_SYM { Lex->one_shot_set= 1; $$= OPT_SESSION; } - ; + /* empty */ { $$= OPT_DEFAULT; } + | ONE_SHOT_SYM { Lex->one_shot_set= 1; $$= OPT_SESSION; } + ; opt_var_type: - /* empty */ { $$=OPT_SESSION; } - | GLOBAL_SYM { $$=OPT_GLOBAL; } - | LOCAL_SYM { $$=OPT_SESSION; } - | SESSION_SYM { $$=OPT_SESSION; } - ; + /* empty */ { $$=OPT_SESSION; } + | GLOBAL_SYM { $$=OPT_GLOBAL; } + | LOCAL_SYM { $$=OPT_SESSION; } + | SESSION_SYM { $$=OPT_SESSION; } + ; opt_var_ident_type: - /* empty */ { $$=OPT_DEFAULT; } - | GLOBAL_SYM '.' { $$=OPT_GLOBAL; } - | LOCAL_SYM '.' { $$=OPT_SESSION; } - | SESSION_SYM '.' { $$=OPT_SESSION; } - ; + /* empty */ { $$=OPT_DEFAULT; } + | GLOBAL_SYM '.' { $$=OPT_GLOBAL; } + | LOCAL_SYM '.' { $$=OPT_SESSION; } + | SESSION_SYM '.' { $$=OPT_SESSION; } + ; ext_option_value: - sys_option_value - | option_type2 option_value; + sys_option_value + | option_type2 option_value + ; sys_option_value: - option_type internal_variable_name equal set_expr_or_default - { - LEX *lex=Lex; - - if ($2.var == trg_new_row_fake_var) + option_type internal_variable_name equal set_expr_or_default { - /* We are in trigger and assigning value to field of new row */ - Item *it; - Item_trigger_field *trg_fld; - sp_instr_set_trigger_field *sp_fld; - LINT_INIT(sp_fld); - if ($1) - { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; - } - if ($4) - it= $4; - else + LEX *lex=Lex; + + if ($2.var == trg_new_row_fake_var) { - /* QQ: Shouldn't this be field's default value ? */ - it= new Item_null(); - } + /* We are in trigger and assigning value to field of new row */ + Item *it; + Item_trigger_field *trg_fld; + sp_instr_set_trigger_field *sp_fld; + LINT_INIT(sp_fld); + if ($1) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } + if ($4) + it= $4; + else + { + /* QQ: Shouldn't this be field's default value ? */ + it= new Item_null(); + } - DBUG_ASSERT(lex->trg_chistics.action_time == TRG_ACTION_BEFORE && - (lex->trg_chistics.event == TRG_EVENT_INSERT || - lex->trg_chistics.event == TRG_EVENT_UPDATE)); - if (!(trg_fld= new Item_trigger_field(Lex->current_context(), - Item_trigger_field::NEW_ROW, - $2.base_name.str, - UPDATE_ACL, FALSE)) || - !(sp_fld= new sp_instr_set_trigger_field(lex->sphead-> - instructions(), - lex->spcont, - trg_fld, - it, lex))) - MYSQL_YYABORT; + DBUG_ASSERT(lex->trg_chistics.action_time == TRG_ACTION_BEFORE && + (lex->trg_chistics.event == TRG_EVENT_INSERT || + lex->trg_chistics.event == TRG_EVENT_UPDATE)); + if (!(trg_fld= new Item_trigger_field(Lex->current_context(), + Item_trigger_field::NEW_ROW, + $2.base_name.str, + UPDATE_ACL, FALSE)) || + !(sp_fld= new sp_instr_set_trigger_field(lex->sphead-> + instructions(), + lex->spcont, + trg_fld, + it, lex))) + MYSQL_YYABORT; - /* - Let us add this item to list of all Item_trigger_field - objects in trigger. - */ - lex->trg_table_fields.link_in_list((uchar *)trg_fld, - (uchar **) &trg_fld-> - next_trg_field); + /* + Let us add this item to list of all Item_trigger_field + objects in trigger. + */ + lex->trg_table_fields.link_in_list((uchar *)trg_fld, + (uchar **) &trg_fld-> + next_trg_field); - lex->sphead->add_instr(sp_fld); - } - else if ($2.var) - { /* System variable */ - if ($1) - lex->option_type= $1; - lex->var_list.push_back(new set_var(lex->option_type, $2.var, - &$2.base_name, $4)); - } - else - { - /* An SP local variable */ - sp_pcontext *ctx= lex->spcont; - sp_variable_t *spv; - sp_instr_set *sp_set; - Item *it; - if ($1) - { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; + lex->sphead->add_instr(sp_fld); } + else if ($2.var) + { /* System variable */ + if ($1) + lex->option_type= $1; + lex->var_list.push_back(new set_var(lex->option_type, $2.var, + &$2.base_name, $4)); + } + else + { + /* An SP local variable */ + sp_pcontext *ctx= lex->spcont; + sp_variable_t *spv; + sp_instr_set *sp_set; + Item *it; + if ($1) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } - spv= ctx->find_variable(&$2.base_name); + spv= ctx->find_variable(&$2.base_name); - if ($4) - it= $4; - else if (spv->dflt) - it= spv->dflt; - else - it= new Item_null(); - sp_set= new sp_instr_set(lex->sphead->instructions(), ctx, - spv->offset, it, spv->type, lex, TRUE); - lex->sphead->add_instr(sp_set); + if ($4) + it= $4; + else if (spv->dflt) + it= spv->dflt; + else + it= new Item_null(); + sp_set= new sp_instr_set(lex->sphead->instructions(), ctx, + spv->offset, it, spv->type, lex, TRUE); + lex->sphead->add_instr(sp_set); + } } - } | option_type TRANSACTION_SYM ISOLATION LEVEL_SYM isolation_types - { - LEX *lex=Lex; - lex->option_type= $1; - lex->var_list.push_back(new set_var(lex->option_type, - find_sys_var(YYTHD, "tx_isolation"), - &null_lex_str, - new Item_int((int32) $5))); - } + { + LEX *lex=Lex; + lex->option_type= $1; + lex->var_list.push_back(new set_var(lex->option_type, + find_sys_var(YYTHD, "tx_isolation"), + &null_lex_str, + new Item_int((int32) $5))); + } ; option_value: - '@' ident_or_text equal expr - { - Lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4))); - } - | '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default - { - LEX *lex=Lex; - lex->var_list.push_back(new set_var($3, $4.var, &$4.base_name, $6)); - } - | charset old_or_new_charset_name_or_default - { - THD *thd= YYTHD; - 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)); - } + '@' ident_or_text equal expr + { + Lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4))); + } + | '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default + { + LEX *lex=Lex; + lex->var_list.push_back(new set_var($3, $4.var, &$4.base_name, $6)); + } + | charset old_or_new_charset_name_or_default + { + THD *thd= YYTHD; + 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)); + } | NAMES_SYM equal expr - { - LEX *lex= Lex; + { + LEX *lex= Lex; sp_pcontext *spc= lex->spcont; - LEX_STRING names; + LEX_STRING names; - names.str= (char *)"names"; - names.length= 5; - if (spc && spc->find_variable(&names)) + names.str= (char *)"names"; + names.length= 5; + if (spc && spc->find_variable(&names)) my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), names.str); else my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; - } - | NAMES_SYM charset_name_or_default opt_collate - { - LEX *lex= Lex; - $2= $2 ? $2 : global_system_variables.character_set_client; - $3= $3 ? $3 : $2; - if (!my_charset_same($2,$3)) - { - my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), - $3->name, $2->csname); - MYSQL_YYABORT; - } - lex->var_list.push_back(new set_var_collation_client($3,$3,$3)); - } - | PASSWORD equal text_or_password - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - LEX_USER *user; + MYSQL_YYABORT; + } + | NAMES_SYM charset_name_or_default opt_collate + { + LEX *lex= Lex; + $2= $2 ? $2 : global_system_variables.character_set_client; + $3= $3 ? $3 : $2; + if (!my_charset_same($2,$3)) + { + my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), + $3->name, $2->csname); + MYSQL_YYABORT; + } + lex->var_list.push_back(new set_var_collation_client($3,$3,$3)); + } + | PASSWORD equal text_or_password + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + LEX_USER *user; sp_pcontext *spc= lex->spcont; - LEX_STRING pw; + LEX_STRING pw; - pw.str= (char *)"password"; - pw.length= 8; - if (spc && spc->find_variable(&pw)) - { + pw.str= (char *)"password"; + pw.length= 8; + if (spc && spc->find_variable(&pw)) + { my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str); - MYSQL_YYABORT; - } - if (!(user=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))) - 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)); - } - | PASSWORD FOR_SYM user equal text_or_password - { - Lex->var_list.push_back(new set_var_password($3,$5)); - } - ; + MYSQL_YYABORT; + } + if (!(user=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))) + 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)); + } + | PASSWORD FOR_SYM user equal text_or_password + { + Lex->var_list.push_back(new set_var_password($3,$5)); + } + ; internal_variable_name: - ident - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - sp_pcontext *spc= lex->spcont; - sp_variable_t *spv; - - /* We have to lookup here since local vars can shadow sysvars */ - if (!spc || !(spv = spc->find_variable(&$1))) - { - /* Not an SP local variable */ - sys_var *tmp=find_sys_var(thd, $1.str, $1.length); - if (!tmp) - MYSQL_YYABORT; - $$.var= tmp; - $$.base_name= null_lex_str; - if (spc && tmp == &sys_autocommit) + ident + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + sp_pcontext *spc= lex->spcont; + sp_variable_t *spv; + + /* We have to lookup here since local vars can shadow sysvars */ + if (!spc || !(spv = spc->find_variable(&$1))) { - /* - We don't allow setting AUTOCOMMIT from a stored function - or trigger. - */ - lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT; - } - } - else - { - /* An SP local variable */ - $$.var= NULL; - $$.base_name= $1; - } - } - | ident '.' ident - { + /* Not an SP local variable */ + sys_var *tmp=find_sys_var(thd, $1.str, $1.length); + if (!tmp) + MYSQL_YYABORT; + $$.var= tmp; + $$.base_name= null_lex_str; + if (spc && tmp == &sys_autocommit) + { + /* + We don't allow setting AUTOCOMMIT from a stored function + or trigger. + */ + lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT; + } + } + else + { + /* An SP local variable */ + $$.var= NULL; + $$.base_name= $1; + } + } + | ident '.' ident + { LEX *lex= Lex; if (check_reserved_words(&$1)) { @@ -10533,523 +11069,533 @@ internal_variable_name: $$.var= tmp; $$.base_name= $1; } - } - | DEFAULT '.' ident - { - sys_var *tmp=find_sys_var(YYTHD, $3.str, $3.length); - if (!tmp) - MYSQL_YYABORT; - if (!tmp->is_struct()) - my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str); - $$.var= tmp; - $$.base_name.str= (char*) "default"; - $$.base_name.length= 7; - } + } + | DEFAULT '.' ident + { + sys_var *tmp=find_sys_var(YYTHD, $3.str, $3.length); + if (!tmp) + MYSQL_YYABORT; + if (!tmp->is_struct()) + my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str); + $$.var= tmp; + $$.base_name.str= (char*) "default"; + $$.base_name.length= 7; + } ; isolation_types: - READ_SYM UNCOMMITTED_SYM { $$= ISO_READ_UNCOMMITTED; } - | READ_SYM COMMITTED_SYM { $$= ISO_READ_COMMITTED; } - | REPEATABLE_SYM READ_SYM { $$= ISO_REPEATABLE_READ; } - | SERIALIZABLE_SYM { $$= ISO_SERIALIZABLE; } - ; + READ_SYM UNCOMMITTED_SYM { $$= ISO_READ_UNCOMMITTED; } + | READ_SYM COMMITTED_SYM { $$= ISO_READ_COMMITTED; } + | REPEATABLE_SYM READ_SYM { $$= ISO_REPEATABLE_READ; } + | SERIALIZABLE_SYM { $$= ISO_SERIALIZABLE; } + ; text_or_password: - TEXT_STRING { $$=$1.str;} - | PASSWORD '(' TEXT_STRING ')' - { - $$= $3.length ? YYTHD->variables.old_passwords ? - Item_func_old_password::alloc(YYTHD, $3.str) : - Item_func_password::alloc(YYTHD, $3.str) : - $3.str; - } - | OLD_PASSWORD '(' TEXT_STRING ')' - { - $$= $3.length ? Item_func_old_password::alloc(YYTHD, $3.str) : - $3.str; - } - ; + TEXT_STRING { $$=$1.str;} + | PASSWORD '(' TEXT_STRING ')' + { + $$= $3.length ? YYTHD->variables.old_passwords ? + Item_func_old_password::alloc(YYTHD, $3.str) : + Item_func_password::alloc(YYTHD, $3.str) : + $3.str; + } + | OLD_PASSWORD '(' TEXT_STRING ')' + { + $$= $3.length ? Item_func_old_password::alloc(YYTHD, $3.str) : + $3.str; + } + ; set_expr_or_default: - expr { $$=$1; } - | DEFAULT { $$=0; } - | ON { $$=new Item_string("ON", 2, system_charset_info); } - | ALL { $$=new Item_string("ALL", 3, system_charset_info); } - | BINARY { $$=new Item_string("binary", 6, system_charset_info); } - ; - + expr { $$=$1; } + | DEFAULT { $$=0; } + | ON { $$=new Item_string("ON", 2, system_charset_info); } + | ALL { $$=new Item_string("ALL", 3, system_charset_info); } + | BINARY { $$=new Item_string("binary", 6, system_charset_info); } + ; /* Lock function */ lock: - LOCK_SYM table_or_tables - { - LEX *lex= Lex; + LOCK_SYM table_or_tables + { + LEX *lex= Lex; - if (lex->sphead) - { - my_error(ER_SP_BADSTATEMENT, MYF(0), "LOCK"); - MYSQL_YYABORT; - } - lex->sql_command= SQLCOM_LOCK_TABLES; - } - table_lock_list - {} - ; + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "LOCK"); + MYSQL_YYABORT; + } + lex->sql_command= SQLCOM_LOCK_TABLES; + } + table_lock_list + {} + ; table_or_tables: - TABLE_SYM - | TABLES; + TABLE_SYM + | TABLES + ; table_lock_list: - table_lock - | table_lock_list ',' table_lock; + table_lock + | table_lock_list ',' table_lock + ; table_lock: - table_ident opt_table_alias lock_option - { - if (!Select->add_table_to_list(YYTHD, $1, $2, 0, (thr_lock_type) $3)) - MYSQL_YYABORT; - } + table_ident opt_table_alias lock_option + { + if (!Select->add_table_to_list(YYTHD, $1, $2, 0, (thr_lock_type) $3)) + MYSQL_YYABORT; + } ; lock_option: - READ_SYM { $$=TL_READ_NO_INSERT; } - | WRITE_SYM { $$=TL_WRITE_DEFAULT; } - | LOW_PRIORITY WRITE_SYM { $$=TL_WRITE_LOW_PRIORITY; } - | READ_SYM LOCAL_SYM { $$= TL_READ; } + READ_SYM { $$= TL_READ_NO_INSERT; } + | WRITE_SYM { $$= TL_WRITE_DEFAULT; } + | LOW_PRIORITY WRITE_SYM { $$= TL_WRITE_LOW_PRIORITY; } + | READ_SYM LOCAL_SYM { $$= TL_READ; } ; unlock: - UNLOCK_SYM - { - LEX *lex= Lex; + UNLOCK_SYM + { + LEX *lex= Lex; - if (lex->sphead) - { - my_error(ER_SP_BADSTATEMENT, MYF(0), "UNLOCK"); - MYSQL_YYABORT; - } - lex->sql_command= SQLCOM_UNLOCK_TABLES; - } - table_or_tables - {} + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "UNLOCK"); + MYSQL_YYABORT; + } + lex->sql_command= SQLCOM_UNLOCK_TABLES; + } + table_or_tables + {} ; - /* ** Handler: direct access to ISAM functions */ handler: - HANDLER_SYM table_ident OPEN_SYM opt_table_alias - { - LEX *lex= Lex; - if (lex->sphead) - { - my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); - MYSQL_YYABORT; - } - lex->sql_command = SQLCOM_HA_OPEN; - if (!lex->current_select->add_table_to_list(lex->thd, $2, $4, 0)) - MYSQL_YYABORT; - } - | HANDLER_SYM table_ident_nodb CLOSE_SYM - { - LEX *lex= Lex; - if (lex->sphead) - { - my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); - MYSQL_YYABORT; - } - lex->sql_command = SQLCOM_HA_CLOSE; - if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0)) - MYSQL_YYABORT; - } - | HANDLER_SYM table_ident_nodb READ_SYM - { - LEX *lex=Lex; - if (lex->sphead) - { - my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); - MYSQL_YYABORT; - } - lex->expr_allows_subselect= FALSE; - lex->sql_command = SQLCOM_HA_READ; - lex->ha_rkey_mode= HA_READ_KEY_EXACT; /* Avoid purify warnings */ - 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)) - MYSQL_YYABORT; - } - handler_read_or_scan where_clause opt_limit_clause - { - Lex->expr_allows_subselect= TRUE; - } + HANDLER_SYM table_ident OPEN_SYM opt_table_alias + { + LEX *lex= Lex; + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); + MYSQL_YYABORT; + } + lex->sql_command = SQLCOM_HA_OPEN; + if (!lex->current_select->add_table_to_list(lex->thd, $2, $4, 0)) + MYSQL_YYABORT; + } + | HANDLER_SYM table_ident_nodb CLOSE_SYM + { + LEX *lex= Lex; + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); + MYSQL_YYABORT; + } + lex->sql_command = SQLCOM_HA_CLOSE; + if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0)) + MYSQL_YYABORT; + } + | HANDLER_SYM table_ident_nodb READ_SYM + { + LEX *lex=Lex; + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); + MYSQL_YYABORT; + } + lex->expr_allows_subselect= FALSE; + lex->sql_command = SQLCOM_HA_READ; + lex->ha_rkey_mode= HA_READ_KEY_EXACT; /* Avoid purify warnings */ + 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)) + MYSQL_YYABORT; + } + handler_read_or_scan where_clause opt_limit_clause + { + Lex->expr_allows_subselect= TRUE; + } ; handler_read_or_scan: - handler_scan_function { Lex->ident= null_lex_str; } + handler_scan_function { Lex->ident= null_lex_str; } | ident handler_rkey_function { Lex->ident= $1; } ; handler_scan_function: - FIRST_SYM { Lex->ha_read_mode = RFIRST; } - | NEXT_SYM { Lex->ha_read_mode = RNEXT; } + FIRST_SYM { Lex->ha_read_mode = RFIRST; } + | NEXT_SYM { Lex->ha_read_mode = RNEXT; } ; handler_rkey_function: - FIRST_SYM { Lex->ha_read_mode = RFIRST; } - | NEXT_SYM { Lex->ha_read_mode = RNEXT; } - | PREV_SYM { Lex->ha_read_mode = RPREV; } - | LAST_SYM { Lex->ha_read_mode = RLAST; } - | handler_rkey_mode - { - LEX *lex=Lex; - lex->ha_read_mode = RKEY; - lex->ha_rkey_mode=$1; - if (!(lex->insert_list = new List_item)) - MYSQL_YYABORT; - } '(' values ')' { } + FIRST_SYM { Lex->ha_read_mode = RFIRST; } + | NEXT_SYM { Lex->ha_read_mode = RNEXT; } + | PREV_SYM { Lex->ha_read_mode = RPREV; } + | LAST_SYM { Lex->ha_read_mode = RLAST; } + | handler_rkey_mode + { + LEX *lex=Lex; + lex->ha_read_mode = RKEY; + lex->ha_rkey_mode=$1; + if (!(lex->insert_list = new List_item)) + MYSQL_YYABORT; + } + '(' values ')' + {} ; handler_rkey_mode: - EQ { $$=HA_READ_KEY_EXACT; } - | GE { $$=HA_READ_KEY_OR_NEXT; } - | LE { $$=HA_READ_KEY_OR_PREV; } - | GT_SYM { $$=HA_READ_AFTER_KEY; } - | LT { $$=HA_READ_BEFORE_KEY; } + EQ { $$=HA_READ_KEY_EXACT; } + | GE { $$=HA_READ_KEY_OR_NEXT; } + | LE { $$=HA_READ_KEY_OR_PREV; } + | GT_SYM { $$=HA_READ_AFTER_KEY; } + | LT { $$=HA_READ_BEFORE_KEY; } ; /* GRANT / REVOKE */ revoke: - REVOKE clear_privileges revoke_command - {} + REVOKE clear_privileges revoke_command + {} ; revoke_command: - grant_privileges ON opt_table grant_ident FROM grant_list - { - LEX *lex= Lex; - lex->sql_command= SQLCOM_REVOKE; - lex->type= 0; - } - | - grant_privileges ON FUNCTION_SYM grant_ident FROM grant_list - { - LEX *lex= Lex; - if (lex->columns.elements) + grant_privileges ON opt_table grant_ident FROM grant_list { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; + LEX *lex= Lex; + lex->sql_command= SQLCOM_REVOKE; + lex->type= 0; } - lex->sql_command= SQLCOM_REVOKE; - lex->type= TYPE_ENUM_FUNCTION; - - } - | - grant_privileges ON PROCEDURE grant_ident FROM grant_list - { - LEX *lex= Lex; - if (lex->columns.elements) + | grant_privileges ON FUNCTION_SYM grant_ident FROM grant_list { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; + LEX *lex= Lex; + if (lex->columns.elements) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } + lex->sql_command= SQLCOM_REVOKE; + lex->type= TYPE_ENUM_FUNCTION; } - lex->sql_command= SQLCOM_REVOKE; - lex->type= TYPE_ENUM_PROCEDURE; - } - | - ALL opt_privileges ',' GRANT OPTION FROM grant_list - { - Lex->sql_command = SQLCOM_REVOKE_ALL; - } - ; + | grant_privileges ON PROCEDURE grant_ident FROM grant_list + { + LEX *lex= Lex; + if (lex->columns.elements) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } + lex->sql_command= SQLCOM_REVOKE; + lex->type= TYPE_ENUM_PROCEDURE; + } + | ALL opt_privileges ',' GRANT OPTION FROM grant_list + { + Lex->sql_command = SQLCOM_REVOKE_ALL; + } + ; grant: - GRANT clear_privileges grant_command - {} + GRANT clear_privileges grant_command + {} ; grant_command: - grant_privileges ON opt_table grant_ident TO_SYM grant_list - require_clause grant_options - { - LEX *lex= Lex; - lex->sql_command= SQLCOM_GRANT; - lex->type= 0; - } - | - grant_privileges ON FUNCTION_SYM grant_ident TO_SYM grant_list - require_clause grant_options - { - LEX *lex= Lex; - if (lex->columns.elements) + grant_privileges ON opt_table grant_ident TO_SYM grant_list + require_clause grant_options { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; + LEX *lex= Lex; + lex->sql_command= SQLCOM_GRANT; + lex->type= 0; } - lex->sql_command= SQLCOM_GRANT; - lex->type= TYPE_ENUM_FUNCTION; - } - | - grant_privileges ON PROCEDURE grant_ident TO_SYM grant_list - require_clause grant_options - { - LEX *lex= Lex; - if (lex->columns.elements) + | grant_privileges ON FUNCTION_SYM grant_ident TO_SYM grant_list + require_clause grant_options { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; + LEX *lex= Lex; + if (lex->columns.elements) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } + lex->sql_command= SQLCOM_GRANT; + lex->type= TYPE_ENUM_FUNCTION; + } + | grant_privileges ON PROCEDURE grant_ident TO_SYM grant_list + require_clause grant_options + { + LEX *lex= Lex; + if (lex->columns.elements) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } + lex->sql_command= SQLCOM_GRANT; + lex->type= TYPE_ENUM_PROCEDURE; } - lex->sql_command= SQLCOM_GRANT; - lex->type= TYPE_ENUM_PROCEDURE; - } ; opt_table: - /* Empty */ - | TABLE_SYM ; - + /* Empty */ + | TABLE_SYM + ; + grant_privileges: - object_privilege_list { } - | ALL opt_privileges - { - Lex->all_privileges= 1; - Lex->grant= GLOBAL_ACLS; - } + object_privilege_list {} + | ALL opt_privileges + { + Lex->all_privileges= 1; + Lex->grant= GLOBAL_ACLS; + } ; opt_privileges: - /* empty */ - | PRIVILEGES - ; + /* empty */ + | PRIVILEGES + ; object_privilege_list: - object_privilege - | object_privilege_list ',' object_privilege; + object_privilege + | object_privilege_list ',' object_privilege + ; object_privilege: - SELECT_SYM { Lex->which_columns = SELECT_ACL;} opt_column_list {} - | INSERT { Lex->which_columns = INSERT_ACL;} opt_column_list {} - | UPDATE_SYM { Lex->which_columns = UPDATE_ACL; } opt_column_list {} - | REFERENCES { Lex->which_columns = REFERENCES_ACL;} opt_column_list {} - | DELETE_SYM { Lex->grant |= DELETE_ACL;} - | USAGE {} - | INDEX_SYM { Lex->grant |= INDEX_ACL;} - | ALTER { Lex->grant |= ALTER_ACL;} - | CREATE { Lex->grant |= CREATE_ACL;} - | DROP { Lex->grant |= DROP_ACL;} - | EXECUTE_SYM { Lex->grant |= EXECUTE_ACL;} - | RELOAD { Lex->grant |= RELOAD_ACL;} - | SHUTDOWN { Lex->grant |= SHUTDOWN_ACL;} - | PROCESS { Lex->grant |= PROCESS_ACL;} - | FILE_SYM { Lex->grant |= FILE_ACL;} - | GRANT OPTION { Lex->grant |= GRANT_ACL;} - | SHOW DATABASES { Lex->grant |= SHOW_DB_ACL;} - | SUPER_SYM { Lex->grant |= SUPER_ACL;} - | CREATE TEMPORARY TABLES { Lex->grant |= CREATE_TMP_ACL;} - | LOCK_SYM TABLES { Lex->grant |= LOCK_TABLES_ACL; } - | REPLICATION SLAVE { Lex->grant |= REPL_SLAVE_ACL; } - | REPLICATION CLIENT_SYM { Lex->grant |= REPL_CLIENT_ACL; } - | CREATE VIEW_SYM { Lex->grant |= CREATE_VIEW_ACL; } - | SHOW VIEW_SYM { Lex->grant |= SHOW_VIEW_ACL; } - | CREATE ROUTINE_SYM { Lex->grant |= CREATE_PROC_ACL; } - | ALTER ROUTINE_SYM { Lex->grant |= ALTER_PROC_ACL; } - | CREATE USER { Lex->grant |= CREATE_USER_ACL; } - | EVENT_SYM { Lex->grant |= EVENT_ACL;} - | TRIGGER_SYM { Lex->grant |= TRIGGER_ACL; } - ; - + SELECT_SYM + { Lex->which_columns = SELECT_ACL;} + opt_column_list {} + | INSERT + { Lex->which_columns = INSERT_ACL;} + opt_column_list {} + | UPDATE_SYM + { Lex->which_columns = UPDATE_ACL; } + opt_column_list {} + | REFERENCES + { Lex->which_columns = REFERENCES_ACL;} + opt_column_list {} + | DELETE_SYM { Lex->grant |= DELETE_ACL;} + | USAGE {} + | INDEX_SYM { Lex->grant |= INDEX_ACL;} + | ALTER { Lex->grant |= ALTER_ACL;} + | CREATE { Lex->grant |= CREATE_ACL;} + | DROP { Lex->grant |= DROP_ACL;} + | EXECUTE_SYM { Lex->grant |= EXECUTE_ACL;} + | RELOAD { Lex->grant |= RELOAD_ACL;} + | SHUTDOWN { Lex->grant |= SHUTDOWN_ACL;} + | PROCESS { Lex->grant |= PROCESS_ACL;} + | FILE_SYM { Lex->grant |= FILE_ACL;} + | GRANT OPTION { Lex->grant |= GRANT_ACL;} + | SHOW DATABASES { Lex->grant |= SHOW_DB_ACL;} + | SUPER_SYM { Lex->grant |= SUPER_ACL;} + | CREATE TEMPORARY TABLES { Lex->grant |= CREATE_TMP_ACL;} + | LOCK_SYM TABLES { Lex->grant |= LOCK_TABLES_ACL; } + | REPLICATION SLAVE { Lex->grant |= REPL_SLAVE_ACL; } + | REPLICATION CLIENT_SYM { Lex->grant |= REPL_CLIENT_ACL; } + | CREATE VIEW_SYM { Lex->grant |= CREATE_VIEW_ACL; } + | SHOW VIEW_SYM { Lex->grant |= SHOW_VIEW_ACL; } + | CREATE ROUTINE_SYM { Lex->grant |= CREATE_PROC_ACL; } + | ALTER ROUTINE_SYM { Lex->grant |= ALTER_PROC_ACL; } + | CREATE USER { Lex->grant |= CREATE_USER_ACL; } + | EVENT_SYM { Lex->grant |= EVENT_ACL;} + | TRIGGER_SYM { Lex->grant |= TRIGGER_ACL; } + ; opt_and: - /* empty */ {} - | AND_SYM {} - ; + /* empty */ {} + | AND_SYM {} + ; require_list: - require_list_element opt_and require_list - | require_list_element - ; + require_list_element opt_and require_list + | require_list_element + ; require_list_element: - SUBJECT_SYM TEXT_STRING - { - LEX *lex=Lex; - if (lex->x509_subject) - { - my_error(ER_DUP_ARGUMENT, MYF(0), "SUBJECT"); - MYSQL_YYABORT; - } - lex->x509_subject=$2.str; - } - | ISSUER_SYM TEXT_STRING - { - LEX *lex=Lex; - if (lex->x509_issuer) - { - my_error(ER_DUP_ARGUMENT, MYF(0), "ISSUER"); - MYSQL_YYABORT; - } - lex->x509_issuer=$2.str; - } - | CIPHER_SYM TEXT_STRING - { - LEX *lex=Lex; - if (lex->ssl_cipher) - { - my_error(ER_DUP_ARGUMENT, MYF(0), "CIPHER"); - MYSQL_YYABORT; - } - lex->ssl_cipher=$2.str; - } - ; + SUBJECT_SYM TEXT_STRING + { + LEX *lex=Lex; + if (lex->x509_subject) + { + my_error(ER_DUP_ARGUMENT, MYF(0), "SUBJECT"); + MYSQL_YYABORT; + } + lex->x509_subject=$2.str; + } + | ISSUER_SYM TEXT_STRING + { + LEX *lex=Lex; + if (lex->x509_issuer) + { + my_error(ER_DUP_ARGUMENT, MYF(0), "ISSUER"); + MYSQL_YYABORT; + } + lex->x509_issuer=$2.str; + } + | CIPHER_SYM TEXT_STRING + { + LEX *lex=Lex; + if (lex->ssl_cipher) + { + my_error(ER_DUP_ARGUMENT, MYF(0), "CIPHER"); + MYSQL_YYABORT; + } + lex->ssl_cipher=$2.str; + } + ; grant_ident: - '*' - { - THD *thd= YYTHD; - LEX *lex= thd->lex; + '*' + { + LEX *lex= Lex; size_t dummy; - if (thd->copy_db_to(&lex->current_select->db, &dummy)) + if (lex->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, + 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)); - MYSQL_YYABORT; - } - } - | ident '.' '*' - { - LEX *lex= Lex; - lex->current_select->db = $1.str; - if (lex->grant == GLOBAL_ACLS) - lex->grant = DB_ACLS & ~GRANT_ACL; - else if (lex->columns.elements) - { - my_message(ER_ILLEGAL_GRANT_FOR_TABLE, + MYSQL_YYABORT; + } + } + | ident '.' '*' + { + LEX *lex= Lex; + lex->current_select->db = $1.str; + 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)); - MYSQL_YYABORT; - } - } - | '*' '.' '*' - { - LEX *lex= Lex; - lex->current_select->db = NULL; - if (lex->grant == GLOBAL_ACLS) - lex->grant= GLOBAL_ACLS & ~GRANT_ACL; - else if (lex->columns.elements) - { - my_message(ER_ILLEGAL_GRANT_FOR_TABLE, + MYSQL_YYABORT; + } + } + | '*' '.' '*' + { + LEX *lex= Lex; + lex->current_select->db = NULL; + if (lex->grant == GLOBAL_ACLS) + lex->grant= GLOBAL_ACLS & ~GRANT_ACL; + else if (lex->columns.elements) + { + my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE), MYF(0)); - MYSQL_YYABORT; - } - } - | table_ident - { - LEX *lex=Lex; - if (!lex->current_select->add_table_to_list(lex->thd, $1,NULL, + MYSQL_YYABORT; + } + } + | table_ident + { + LEX *lex=Lex; + if (!lex->current_select->add_table_to_list(lex->thd, $1,NULL, TL_OPTION_UPDATING)) - MYSQL_YYABORT; - if (lex->grant == GLOBAL_ACLS) - lex->grant = TABLE_ACLS & ~GRANT_ACL; - } - ; - + MYSQL_YYABORT; + if (lex->grant == GLOBAL_ACLS) + lex->grant = TABLE_ACLS & ~GRANT_ACL; + } + ; user_list: - user { if (Lex->users_list.push_back($1)) MYSQL_YYABORT;} - | user_list ',' user - { - if (Lex->users_list.push_back($3)) - MYSQL_YYABORT; - } - ; - + user + { + if (Lex->users_list.push_back($1)) + MYSQL_YYABORT; + } + | user_list ',' user + { + if (Lex->users_list.push_back($3)) + MYSQL_YYABORT; + } + ; grant_list: - grant_user { if (Lex->users_list.push_back($1)) MYSQL_YYABORT;} - | grant_list ',' grant_user - { - if (Lex->users_list.push_back($3)) - MYSQL_YYABORT; - } - ; - + grant_user + { + if (Lex->users_list.push_back($1)) + MYSQL_YYABORT; + } + | grant_list ',' grant_user + { + if (Lex->users_list.push_back($3)) + MYSQL_YYABORT; + } + ; grant_user: - user IDENTIFIED_SYM BY TEXT_STRING - { - $$=$1; $1->password=$4; - if ($4.length) - { - if (YYTHD->variables.old_passwords) - { - char *buff= - (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1); - if (buff) - make_scrambled_password_323(buff, $4.str); - $1->password.str= buff; - $1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; - } - else - { - char *buff= - (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1); - if (buff) - make_scrambled_password(buff, $4.str); - $1->password.str= buff; - $1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH; - } - } - } - | user IDENTIFIED_SYM BY PASSWORD TEXT_STRING - { $$= $1; $1->password= $5; } - | user - { $$= $1; $1->password= null_lex_str; } + user IDENTIFIED_SYM BY TEXT_STRING + { + $$=$1; $1->password=$4; + if ($4.length) + { + if (YYTHD->variables.old_passwords) + { + char *buff= + (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1); + if (buff) + make_scrambled_password_323(buff, $4.str); + $1->password.str= buff; + $1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; + } + else + { + char *buff= + (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1); + if (buff) + make_scrambled_password(buff, $4.str); + $1->password.str= buff; + $1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH; + } + } + } + | user IDENTIFIED_SYM BY PASSWORD TEXT_STRING + { $$= $1; $1->password= $5; } + | user + { $$= $1; $1->password= null_lex_str; } ; - opt_column_list: - /* empty */ - { - LEX *lex=Lex; - lex->grant |= lex->which_columns; - } - | '(' column_list ')'; + /* empty */ + { + LEX *lex=Lex; + lex->grant |= lex->which_columns; + } + | '(' column_list ')' + ; column_list: - column_list ',' column_list_id - | column_list_id; + column_list ',' column_list_id + | column_list_id + ; column_list_id: - ident - { - String *new_str = new (YYTHD->mem_root) String((const char*) $1.str,$1.length,system_charset_info); - List_iterator <LEX_COLUMN> iter(Lex->columns); - class LEX_COLUMN *point; - LEX *lex=Lex; - while ((point=iter++)) - { - if (!my_strcasecmp(system_charset_info, - point->column.ptr(), new_str->ptr())) - break; - } - lex->grant_tot_col|= lex->which_columns; - if (point) - point->rights |= lex->which_columns; - else - lex->columns.push_back(new LEX_COLUMN (*new_str,lex->which_columns)); - } + ident + { + String *new_str = new (YYTHD->mem_root) String((const char*) $1.str,$1.length,system_charset_info); + List_iterator <LEX_COLUMN> iter(Lex->columns); + class LEX_COLUMN *point; + LEX *lex=Lex; + while ((point=iter++)) + { + if (!my_strcasecmp(system_charset_info, + point->column.ptr(), new_str->ptr())) + break; + } + lex->grant_tot_col|= lex->which_columns; + if (point) + point->rights |= lex->which_columns; + else + lex->columns.push_back(new LEX_COLUMN (*new_str,lex->which_columns)); + } ; - -require_clause: /* empty */ +require_clause: + /* empty */ | REQUIRE_SYM require_list { Lex->ssl_type=SSL_TYPE_SPECIFIED; @@ -11062,220 +11608,223 @@ require_clause: /* empty */ { Lex->ssl_type=SSL_TYPE_X509; } - | REQUIRE_SYM NONE_SYM - { - Lex->ssl_type=SSL_TYPE_NONE; - } - ; + | REQUIRE_SYM NONE_SYM + { + Lex->ssl_type=SSL_TYPE_NONE; + } + ; grant_options: - /* empty */ {} - | WITH grant_option_list; + /* empty */ {} + | WITH grant_option_list + ; grant_option_list: - grant_option_list grant_option {} - | grant_option {} + grant_option_list grant_option {} + | grant_option {} ; grant_option: - GRANT OPTION { Lex->grant |= GRANT_ACL;} + GRANT OPTION { Lex->grant |= GRANT_ACL;} | MAX_QUERIES_PER_HOUR ulong_num - { - LEX *lex=Lex; - lex->mqh.questions=$2; - lex->mqh.specified_limits|= USER_RESOURCES::QUERIES_PER_HOUR; - } + { + LEX *lex=Lex; + lex->mqh.questions=$2; + lex->mqh.specified_limits|= USER_RESOURCES::QUERIES_PER_HOUR; + } | MAX_UPDATES_PER_HOUR ulong_num - { - LEX *lex=Lex; - lex->mqh.updates=$2; - lex->mqh.specified_limits|= USER_RESOURCES::UPDATES_PER_HOUR; - } + { + LEX *lex=Lex; + lex->mqh.updates=$2; + lex->mqh.specified_limits|= USER_RESOURCES::UPDATES_PER_HOUR; + } | MAX_CONNECTIONS_PER_HOUR ulong_num - { - LEX *lex=Lex; - lex->mqh.conn_per_hour= $2; - lex->mqh.specified_limits|= USER_RESOURCES::CONNECTIONS_PER_HOUR; - } + { + LEX *lex=Lex; + lex->mqh.conn_per_hour= $2; + lex->mqh.specified_limits|= USER_RESOURCES::CONNECTIONS_PER_HOUR; + } | MAX_USER_CONNECTIONS_SYM ulong_num - { - LEX *lex=Lex; - lex->mqh.user_conn= $2; - lex->mqh.specified_limits|= USER_RESOURCES::USER_CONNECTIONS; - } + { + LEX *lex=Lex; + lex->mqh.user_conn= $2; + lex->mqh.specified_limits|= USER_RESOURCES::USER_CONNECTIONS; + } ; begin: - BEGIN_SYM - { - LEX *lex=Lex; - lex->sql_command = SQLCOM_BEGIN; - lex->start_transaction_opt= 0; - } - opt_work {} - ; + BEGIN_SYM + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_BEGIN; + lex->start_transaction_opt= 0; + } + opt_work {} + ; opt_work: - /* empty */ {} - | WORK_SYM {} + /* empty */ {} + | WORK_SYM {} ; opt_chain: - /* empty */ { $$= (YYTHD->variables.completion_type == 1); } - | AND_SYM NO_SYM CHAIN_SYM { $$=0; } - | AND_SYM CHAIN_SYM { $$=1; } - ; + /* empty */ + { $$= (YYTHD->variables.completion_type == 1); } + | AND_SYM NO_SYM CHAIN_SYM { $$=0; } + | AND_SYM CHAIN_SYM { $$=1; } + ; opt_release: - /* empty */ { $$= (YYTHD->variables.completion_type == 2); } - | RELEASE_SYM { $$=1; } - | NO_SYM RELEASE_SYM { $$=0; } - ; - + /* empty */ + { $$= (YYTHD->variables.completion_type == 2); } + | RELEASE_SYM { $$=1; } + | NO_SYM RELEASE_SYM { $$=0; } +; + opt_savepoint: - /* empty */ {} - | SAVEPOINT_SYM {} - ; + /* empty */ {} + | SAVEPOINT_SYM {} + ; commit: - COMMIT_SYM opt_work opt_chain opt_release - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_COMMIT; - lex->tx_chain= $3; - lex->tx_release= $4; - } - ; + COMMIT_SYM opt_work opt_chain opt_release + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_COMMIT; + lex->tx_chain= $3; + lex->tx_release= $4; + } + ; rollback: - ROLLBACK_SYM opt_work opt_chain opt_release - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_ROLLBACK; - lex->tx_chain= $3; - lex->tx_release= $4; - } - | ROLLBACK_SYM opt_work - TO_SYM opt_savepoint ident - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_ROLLBACK_TO_SAVEPOINT; - lex->ident= $5; - } - ; + ROLLBACK_SYM opt_work opt_chain opt_release + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_ROLLBACK; + lex->tx_chain= $3; + lex->tx_release= $4; + } + | ROLLBACK_SYM opt_work + TO_SYM opt_savepoint ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_ROLLBACK_TO_SAVEPOINT; + lex->ident= $5; + } + ; savepoint: - SAVEPOINT_SYM ident - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_SAVEPOINT; - lex->ident= $2; - } - ; + SAVEPOINT_SYM ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SAVEPOINT; + lex->ident= $2; + } + ; release: - RELEASE_SYM SAVEPOINT_SYM ident - { - LEX *lex=Lex; - lex->sql_command= SQLCOM_RELEASE_SAVEPOINT; - lex->ident= $3; - } - ; - + RELEASE_SYM SAVEPOINT_SYM ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_RELEASE_SAVEPOINT; + lex->ident= $3; + } + ; + /* UNIONS : glue selects together */ union_clause: - /* empty */ {} - | union_list - ; + /* empty */ {} + | union_list + ; union_list: - UNION_SYM union_option - { - LEX *lex=Lex; - if (lex->result) - { - /* Only the last SELECT can have INTO...... */ - my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO"); - MYSQL_YYABORT; - } - if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) - { - 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)) - MYSQL_YYABORT; - mysql_init_select(lex); - lex->current_select->linkage=UNION_TYPE; - if ($2) /* UNION DISTINCT - remember position */ - lex->current_select->master_unit()->union_distinct= - lex->current_select; - } - select_init - { - /* - Remove from the name resolution context stack the context of the - last select in the union. - */ - Lex->pop_context(); - } - ; + UNION_SYM union_option + { + LEX *lex=Lex; + if (lex->result) + { + /* Only the last SELECT can have INTO...... */ + my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO"); + MYSQL_YYABORT; + } + if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) + { + 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)) + MYSQL_YYABORT; + mysql_init_select(lex); + lex->current_select->linkage=UNION_TYPE; + if ($2) /* UNION DISTINCT - remember position */ + lex->current_select->master_unit()->union_distinct= + lex->current_select; + } + select_init + { + /* + Remove from the name resolution context stack the context of the + last select in the union. + */ + Lex->pop_context(); + } + ; union_opt: - /* Empty */ { $$= 0; } - | union_list { $$= 1; } - | union_order_or_limit { $$= 1; } - ; + /* Empty */ { $$= 0; } + | union_list { $$= 1; } + | union_order_or_limit { $$= 1; } + ; union_order_or_limit: - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - DBUG_ASSERT(lex->current_select->linkage != GLOBAL_OPTIONS_TYPE); - SELECT_LEX *sel= lex->current_select; - SELECT_LEX_UNIT *unit= sel->master_unit(); - SELECT_LEX *fake= unit->fake_select_lex; - if (fake) - { - unit->global_parameters= fake; - fake->no_table_names_allowed= 1; - lex->current_select= fake; - } - thd->where= "global ORDER clause"; - } - order_or_limit - { - THD *thd= YYTHD; - thd->lex->current_select->no_table_names_allowed= 0; - thd->where= ""; + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + DBUG_ASSERT(lex->current_select->linkage != GLOBAL_OPTIONS_TYPE); + SELECT_LEX *sel= lex->current_select; + SELECT_LEX_UNIT *unit= sel->master_unit(); + SELECT_LEX *fake= unit->fake_select_lex; + if (fake) + { + unit->global_parameters= fake; + fake->no_table_names_allowed= 1; + lex->current_select= fake; + } + thd->where= "global ORDER clause"; } - ; + order_or_limit + { + THD *thd= YYTHD; + thd->lex->current_select->no_table_names_allowed= 0; + thd->where= ""; + } + ; order_or_limit: - order_clause opt_limit_clause_init - | limit_clause - ; + order_clause opt_limit_clause_init + | limit_clause + ; union_option: - /* empty */ { $$=1; } - | DISTINCT { $$=1; } - | ALL { $$=0; } + /* empty */ { $$=1; } + | DISTINCT { $$=1; } + | ALL { $$=0; } ; subselect: - SELECT_SYM subselect_start subselect_init subselect_end - { - $$= $3; - } + SELECT_SYM subselect_start subselect_init subselect_end + { + $$= $3; + } | '(' subselect_start subselect ')' { - THD *thd= YYTHD; + THD *thd= YYTHD; /* note that a local variable can't be used for $3 as it's used in local variable construction @@ -11290,49 +11839,53 @@ subselect: while ((item= it++)) add_item_to_list(thd, item); } - union_clause subselect_end { $$= $3; }; + union_clause subselect_end { $$= $3; } + ; subselect_init: - select_init2 - { - $$= Lex->current_select->master_unit()->first_select(); - }; + select_init2 + { + $$= Lex->current_select->master_unit()->first_select(); + } + ; subselect_start: - { - LEX *lex=Lex; - if (!lex->expr_allows_subselect || - lex->sql_command == (int)SQLCOM_PURGE) - { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; - } - /* - we are making a "derived table" for the parenthesis - as we need to have a lex level to fit the union - after the parenthesis, e.g. - (SELECT .. ) UNION ... becomes - SELECT * FROM ((SELECT ...) UNION ...) - */ - if (mysql_new_select(Lex, 1)) - MYSQL_YYABORT; - }; + { + LEX *lex=Lex; + if (!lex->expr_allows_subselect || + lex->sql_command == (int)SQLCOM_PURGE) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } + /* + we are making a "derived table" for the parenthesis + as we need to have a lex level to fit the union + after the parenthesis, e.g. + (SELECT .. ) UNION ... becomes + SELECT * FROM ((SELECT ...) UNION ...) + */ + if (mysql_new_select(Lex, 1)) + MYSQL_YYABORT; + } + ; subselect_end: - { - LEX *lex=Lex; - lex->pop_context(); - SELECT_LEX *child= lex->current_select; - 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+= + { + LEX *lex=Lex; + lex->pop_context(); + SELECT_LEX *child= lex->current_select; + 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; - }; + } + ; /************************************************************************** @@ -11341,22 +11894,22 @@ subselect_end: **************************************************************************/ view_or_trigger_or_sp_or_event: - definer view_or_trigger_or_sp_or_event_tail - {} - | view_replace_or_algorithm definer view_tail - {} - ; + definer view_or_trigger_or_sp_or_event_tail + {} + | view_replace_or_algorithm definer view_tail + {} + ; view_or_trigger_or_sp_or_event_tail: - view_tail - {} - | trigger_tail - {} - | sp_tail - {} - | event_tail - {} - ; + view_tail + {} + | trigger_tail + {} + | sp_tail + {} + | event_tail + {} + ; /************************************************************************** @@ -11365,22 +11918,22 @@ view_or_trigger_or_sp_or_event_tail: **************************************************************************/ definer: - /* empty */ - { - /* - We have to distinguish missing DEFINER-clause from case when - CURRENT_USER specified as definer explicitly in order to properly - handle CREATE TRIGGER statements which come to replication thread - from older master servers (i.e. to create non-suid trigger in this - case). - */ - YYTHD->lex->definer= 0; - } - | DEFINER_SYM EQ user - { - YYTHD->lex->definer= get_current_user(YYTHD, $3); - } - ; + /* empty */ + { + /* + We have to distinguish missing DEFINER-clause from case when + CURRENT_USER specified as definer explicitly in order to properly + handle CREATE TRIGGER statements which come to replication thread + from older master servers (i.e. to create non-suid trigger in this + case). + */ + YYTHD->lex->definer= 0; + } + | DEFINER_SYM EQ user + { + YYTHD->lex->definer= get_current_user(YYTHD, $3); + } + ; /************************************************************************** @@ -11389,128 +11942,128 @@ definer: **************************************************************************/ view_replace_or_algorithm: - view_replace - {} - | view_replace view_algorithm - {} - | view_algorithm - {} - ; + view_replace + {} + | view_replace view_algorithm + {} + | view_algorithm + {} + ; view_replace: - OR_SYM REPLACE - { Lex->create_view_mode= VIEW_CREATE_OR_REPLACE; } - ; + OR_SYM REPLACE + { Lex->create_view_mode= VIEW_CREATE_OR_REPLACE; } + ; view_algorithm: - ALGORITHM_SYM EQ UNDEFINED_SYM - { Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; } - | ALGORITHM_SYM EQ MERGE_SYM - { Lex->create_view_algorithm= VIEW_ALGORITHM_MERGE; } - | ALGORITHM_SYM EQ TEMPTABLE_SYM - { Lex->create_view_algorithm= VIEW_ALGORITHM_TMPTABLE; } - ; + ALGORITHM_SYM EQ UNDEFINED_SYM + { Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; } + | ALGORITHM_SYM EQ MERGE_SYM + { Lex->create_view_algorithm= VIEW_ALGORITHM_MERGE; } + | ALGORITHM_SYM EQ TEMPTABLE_SYM + { Lex->create_view_algorithm= VIEW_ALGORITHM_TMPTABLE; } + ; view_suid: - /* empty */ - { Lex->create_view_suid= VIEW_SUID_DEFAULT; } - | SQL_SYM SECURITY_SYM DEFINER_SYM - { Lex->create_view_suid= VIEW_SUID_DEFINER; } - | SQL_SYM SECURITY_SYM INVOKER_SYM - { Lex->create_view_suid= VIEW_SUID_INVOKER; } - ; + /* empty */ + { Lex->create_view_suid= VIEW_SUID_DEFAULT; } + | SQL_SYM SECURITY_SYM DEFINER_SYM + { Lex->create_view_suid= VIEW_SUID_DEFINER; } + | SQL_SYM SECURITY_SYM INVOKER_SYM + { Lex->create_view_suid= VIEW_SUID_INVOKER; } + ; view_tail: - view_suid VIEW_SYM table_ident - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - 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)) - MYSQL_YYABORT; - } - view_list_opt AS - { - THD *thd= YYTHD; - Lex_input_stream *lip= thd->m_lip; + view_suid VIEW_SYM table_ident + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + 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)) + MYSQL_YYABORT; + } + view_list_opt AS + { + THD *thd= YYTHD; + Lex_input_stream *lip= thd->m_lip; - lip->body_utf8_start(thd, lip->get_cpp_ptr()); - } - view_select - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - Lex_input_stream *lip= thd->m_lip; + lip->body_utf8_start(thd, lip->get_cpp_ptr()); + } + view_select + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; - lip->body_utf8_append(lip->get_cpp_ptr()); + lip->body_utf8_append(lip->get_cpp_ptr()); - lex->view_body_utf8.str= thd->strmake(lip->get_body_utf8_str(), - lip->get_body_utf8_length()); - lex->view_body_utf8.length= lip->get_body_utf8_length(); + lex->view_body_utf8.str= thd->strmake(lip->get_body_utf8_str(), + lip->get_body_utf8_length()); + lex->view_body_utf8.length= lip->get_body_utf8_length(); - trim_whitespace(&my_charset_utf8_general_ci, &lex->view_body_utf8); - } - ; + trim_whitespace(&my_charset_utf8_general_ci, &lex->view_body_utf8); + } + ; view_list_opt: - /* empty */ - {} - | '(' view_list ')' - ; + /* empty */ + {} + | '(' view_list ')' + ; view_list: - ident - { - Lex->view_list.push_back((LEX_STRING*) - sql_memdup(&$1, sizeof(LEX_STRING))); - } - | view_list ',' ident - { - Lex->view_list.push_back((LEX_STRING*) - sql_memdup(&$3, sizeof(LEX_STRING))); - } - ; + ident + { + Lex->view_list.push_back((LEX_STRING*) + sql_memdup(&$1, sizeof(LEX_STRING))); + } + | view_list ',' ident + { + Lex->view_list.push_back((LEX_STRING*) + sql_memdup(&$3, sizeof(LEX_STRING))); + } + ; view_select: - { - THD *thd= YYTHD; - LEX *lex= Lex; - Lex_input_stream *lip= thd->m_lip; - 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; - lex->create_view_select_start= lip->get_cpp_ptr(); - } - view_select_aux view_check_option - { - THD *thd= YYTHD; - LEX *lex= Lex; - Lex_input_stream *lip= thd->m_lip; - 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; - lex->create_view_select_end= lip->get_cpp_ptr(); - } + { + THD *thd= YYTHD; + LEX *lex= Lex; + Lex_input_stream *lip= thd->m_lip; + 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; + lex->create_view_select_start= lip->get_cpp_ptr(); + } + view_select_aux view_check_option + { + THD *thd= YYTHD; + LEX *lex= Lex; + Lex_input_stream *lip= thd->m_lip; + 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; + lex->create_view_select_end= lip->get_cpp_ptr(); + } ; view_select_aux: - SELECT_SYM select_init2 + SELECT_SYM select_init2 | '(' select_paren ')' union_opt ; view_check_option: - /* empty */ - { Lex->create_view_check= VIEW_CHECK_NONE; } - | WITH CHECK_SYM OPTION - { Lex->create_view_check= VIEW_CHECK_CASCADED; } - | WITH CASCADED CHECK_SYM OPTION - { Lex->create_view_check= VIEW_CHECK_CASCADED; } - | WITH LOCAL_SYM CHECK_SYM OPTION - { Lex->create_view_check= VIEW_CHECK_LOCAL; } - ; + /* empty */ + { Lex->create_view_check= VIEW_CHECK_NONE; } + | WITH CHECK_SYM OPTION + { Lex->create_view_check= VIEW_CHECK_CASCADED; } + | WITH CASCADED CHECK_SYM OPTION + { Lex->create_view_check= VIEW_CHECK_CASCADED; } + | WITH LOCAL_SYM CHECK_SYM OPTION + { Lex->create_view_check= VIEW_CHECK_LOCAL; } + ; /************************************************************************** @@ -11519,93 +12072,93 @@ view_check_option: **************************************************************************/ trigger_tail: - TRIGGER_SYM - remember_name - sp_name - trg_action_time - trg_event - ON - remember_name /* $7 */ - { /* $8 */ - THD *thd= YYTHD; - LEX *lex= thd->lex; - Lex_input_stream *lip= thd->m_lip; - lex->raw_trg_on_table_name_begin= lip->get_tok_start(); - } - table_ident /* $9 */ - FOR_SYM - remember_name /* $11 */ - { /* $12 */ - THD *thd= YYTHD; - LEX *lex= thd->lex; - Lex_input_stream *lip= thd->m_lip; - lex->raw_trg_on_table_name_end= lip->get_tok_start(); - } - EACH_SYM - ROW_SYM - { /* $15 */ - THD *thd= YYTHD; - LEX *lex= thd->lex; - Lex_input_stream *lip= thd->m_lip; - sp_head *sp; - - if (lex->sphead) - { - my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "TRIGGER"); - MYSQL_YYABORT; - } + TRIGGER_SYM + remember_name + sp_name + trg_action_time + trg_event + ON + remember_name /* $7 */ + { /* $8 */ + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; + lex->raw_trg_on_table_name_begin= lip->get_tok_start(); + } + table_ident /* $9 */ + FOR_SYM + remember_name /* $11 */ + { /* $12 */ + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; + lex->raw_trg_on_table_name_end= lip->get_tok_start(); + } + EACH_SYM + ROW_SYM + { /* $15 */ + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; + sp_head *sp; - if (!(sp= new sp_head())) - MYSQL_YYABORT; - sp->reset_thd_mem_root(thd); - sp->init(lex); - sp->init_sp_name(thd, $3); - lex->stmt_definition_begin= $2; - lex->ident.str= $7; - lex->ident.length= $11 - $7; - - sp->m_type= TYPE_ENUM_TRIGGER; - lex->sphead= sp; - lex->spname= $3; - /* - We have to turn of CLIENT_MULTI_QUERIES while parsing a - stored procedure, otherwise yylex will chop it into pieces - at each ';'. - */ - $<ulong_num>$= thd->client_capabilities & CLIENT_MULTI_QUERIES; - thd->client_capabilities &= ~CLIENT_MULTI_QUERIES; - - bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); - lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->set_body_start(thd, lip->get_cpp_ptr()); - } - sp_proc_stmt /* $16 */ - { /* $17 */ - LEX *lex= Lex; - sp_head *sp= lex->sphead; - - lex->sql_command= SQLCOM_CREATE_TRIGGER; - sp->set_stmt_end(YYTHD); - /* Restore flag if it was cleared above */ - - YYTHD->client_capabilities |= $<ulong_num>15; - sp->restore_thd_mem_root(YYTHD); - - if (sp->is_not_allowed_in_function("trigger")) - MYSQL_YYABORT; - - /* - We have to do it after parsing trigger body, because some of - sp_proc_stmt alternatives are not saving/restoring LEX, so - lex->query_tables can be wiped out. - */ - if (!lex->select_lex.add_table_to_list(YYTHD, $9, - (LEX_STRING*) 0, - TL_OPTION_UPDATING, - TL_IGNORE)) - MYSQL_YYABORT; - } - ; + if (lex->sphead) + { + my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "TRIGGER"); + MYSQL_YYABORT; + } + + if (!(sp= new sp_head())) + MYSQL_YYABORT; + sp->reset_thd_mem_root(thd); + sp->init(lex); + sp->m_type= TYPE_ENUM_TRIGGER; + sp->init_sp_name(thd, $3); + lex->stmt_definition_begin= $2; + lex->ident.str= $7; + lex->ident.length= $11 - $7; + + lex->sphead= sp; + lex->spname= $3; + /* + We have to turn of CLIENT_MULTI_QUERIES while parsing a + stored procedure, otherwise yylex will chop it into pieces + at each ';'. + */ + $<ulong_num>$= thd->client_capabilities & CLIENT_MULTI_QUERIES; + thd->client_capabilities &= ~CLIENT_MULTI_QUERIES; + + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + lex->sphead->m_chistics= &lex->sp_chistics; + lex->sphead->set_body_start(thd, lip->get_cpp_ptr()); + } + sp_proc_stmt /* $16 */ + { /* $17 */ + LEX *lex= Lex; + sp_head *sp= lex->sphead; + + lex->sql_command= SQLCOM_CREATE_TRIGGER; + sp->set_stmt_end(YYTHD); + /* Restore flag if it was cleared above */ + + YYTHD->client_capabilities |= $<ulong_num>15; + sp->restore_thd_mem_root(YYTHD); + + if (sp->is_not_allowed_in_function("trigger")) + MYSQL_YYABORT; + + /* + We have to do it after parsing trigger body, because some of + sp_proc_stmt alternatives are not saving/restoring LEX, so + lex->query_tables can be wiped out. + */ + if (!lex->select_lex.add_table_to_list(YYTHD, $9, + (LEX_STRING*) 0, + TL_OPTION_UPDATING, + TL_IGNORE)) + MYSQL_YYABORT; + } + ; /************************************************************************** @@ -11614,181 +12167,192 @@ trigger_tail: **************************************************************************/ sp_tail: - udf_func_type remember_name FUNCTION_SYM sp_name - { - LEX *lex=Lex; - lex->udf.type= $1; - lex->stmt_definition_begin= $2; - lex->spname= $4; - } - create_function_tail - {} - | PROCEDURE remember_name sp_name - { - LEX *lex= Lex; - sp_head *sp; + udf_func_type remember_name FUNCTION_SYM sp_name + { + LEX *lex=Lex; + lex->udf.type= $1; + lex->stmt_definition_begin= $2; + lex->spname= $4; + } + create_function_tail + {} + | PROCEDURE remember_name sp_name + { + LEX *lex= Lex; + sp_head *sp; - if (lex->sphead) - { - my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE"); - MYSQL_YYABORT; - } - - lex->stmt_definition_begin= $2; - - /* Order is important here: new - reset - init */ - sp= new sp_head(); - sp->reset_thd_mem_root(YYTHD); - sp->init(lex); - sp->init_sp_name(YYTHD, $3); - - sp->m_type= TYPE_ENUM_PROCEDURE; - lex->sphead= sp; - /* - * We have to turn of CLIENT_MULTI_QUERIES while parsing a - * stored procedure, otherwise yylex will chop it into pieces - * at each ';'. - */ - $<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; - YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); - } - '(' - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - Lex_input_stream *lip= thd->m_lip; - const char* tmp_param_begin; - - tmp_param_begin= lip->get_cpp_tok_start(); - tmp_param_begin++; - lex->sphead->m_param_begin= tmp_param_begin; - } - sp_pdparam_list - ')' - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - Lex_input_stream *lip= thd->m_lip; + if (lex->sphead) + { + my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE"); + MYSQL_YYABORT; + } - lex->sphead->m_param_end= lip->get_cpp_tok_start(); - bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); - } - sp_c_chistics - { - THD *thd= YYTHD; - LEX *lex= thd->lex; - Lex_input_stream *lip= thd->m_lip; + lex->stmt_definition_begin= $2; - lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); - } - sp_proc_stmt - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; + /* Order is important here: new - reset - init */ + sp= new sp_head(); + sp->reset_thd_mem_root(YYTHD); + sp->init(lex); + sp->m_type= TYPE_ENUM_PROCEDURE; + sp->init_sp_name(YYTHD, $3); - sp->set_stmt_end(YYTHD); - lex->sql_command= SQLCOM_CREATE_PROCEDURE; - /* - Restore flag if it was cleared above - Be careful with counting. the block where we save the value - is $4. - */ - YYTHD->client_capabilities |= $<ulong_num>4; - sp->restore_thd_mem_root(YYTHD); - } - ; + lex->sphead= sp; + /* + * We have to turn of CLIENT_MULTI_QUERIES while parsing a + * stored procedure, otherwise yylex will chop it into pieces + * at each ';'. + */ + $<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; + YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); + } + '(' + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; + const char* tmp_param_begin; + + tmp_param_begin= lip->get_cpp_tok_start(); + tmp_param_begin++; + lex->sphead->m_param_begin= tmp_param_begin; + } + sp_pdparam_list + ')' + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; + + lex->sphead->m_param_end= lip->get_cpp_tok_start(); + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + } + sp_c_chistics + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; + + lex->sphead->m_chistics= &lex->sp_chistics; + lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); + } + sp_proc_stmt + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + + sp->set_stmt_end(YYTHD); + lex->sql_command= SQLCOM_CREATE_PROCEDURE; + /* + Restore flag if it was cleared above + Be careful with counting. the block where we save the value + is $4. + */ + YYTHD->client_capabilities |= $<ulong_num>4; + sp->restore_thd_mem_root(YYTHD); + } + ; /*************************************************************************/ -xa: XA_SYM begin_or_start xid opt_join_or_resume - { - Lex->sql_command = SQLCOM_XA_START; - } - | XA_SYM END xid opt_suspend - { - Lex->sql_command = SQLCOM_XA_END; - } - | XA_SYM PREPARE_SYM xid - { - Lex->sql_command = SQLCOM_XA_PREPARE; - } - | XA_SYM COMMIT_SYM xid opt_one_phase - { - Lex->sql_command = SQLCOM_XA_COMMIT; - } - | XA_SYM ROLLBACK_SYM xid - { - Lex->sql_command = SQLCOM_XA_ROLLBACK; - } - | XA_SYM RECOVER_SYM - { - Lex->sql_command = SQLCOM_XA_RECOVER; - } - ; - -xid: text_string - { - MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE); - if (!(Lex->xid=(XID *)YYTHD->alloc(sizeof(XID)))) - MYSQL_YYABORT; - Lex->xid->set(1L, $1->ptr(), $1->length(), 0, 0); - } - | text_string ',' text_string - { - MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE); - if (!(Lex->xid=(XID *)YYTHD->alloc(sizeof(XID)))) - MYSQL_YYABORT; - Lex->xid->set(1L, $1->ptr(), $1->length(), $3->ptr(), $3->length()); - } - | text_string ',' text_string ',' ulong_num - { - MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE); - if (!(Lex->xid=(XID *)YYTHD->alloc(sizeof(XID)))) - MYSQL_YYABORT; - Lex->xid->set($5, $1->ptr(), $1->length(), $3->ptr(), $3->length()); - } - ; - -begin_or_start: BEGIN_SYM {} - | START_SYM {} - ; +xa: + XA_SYM begin_or_start xid opt_join_or_resume + { + Lex->sql_command = SQLCOM_XA_START; + } + | XA_SYM END xid opt_suspend + { + Lex->sql_command = SQLCOM_XA_END; + } + | XA_SYM PREPARE_SYM xid + { + Lex->sql_command = SQLCOM_XA_PREPARE; + } + | XA_SYM COMMIT_SYM xid opt_one_phase + { + Lex->sql_command = SQLCOM_XA_COMMIT; + } + | XA_SYM ROLLBACK_SYM xid + { + Lex->sql_command = SQLCOM_XA_ROLLBACK; + } + | XA_SYM RECOVER_SYM + { + Lex->sql_command = SQLCOM_XA_RECOVER; + } + ; + +xid: + text_string + { + MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE); + if (!(Lex->xid=(XID *)YYTHD->alloc(sizeof(XID)))) + MYSQL_YYABORT; + Lex->xid->set(1L, $1->ptr(), $1->length(), 0, 0); + } + | text_string ',' text_string + { + MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE); + if (!(Lex->xid=(XID *)YYTHD->alloc(sizeof(XID)))) + MYSQL_YYABORT; + Lex->xid->set(1L, $1->ptr(), $1->length(), $3->ptr(), $3->length()); + } + | text_string ',' text_string ',' ulong_num + { + MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE); + if (!(Lex->xid=(XID *)YYTHD->alloc(sizeof(XID)))) + MYSQL_YYABORT; + Lex->xid->set($5, $1->ptr(), $1->length(), $3->ptr(), $3->length()); + } + ; + +begin_or_start: + BEGIN_SYM {} + | START_SYM {} + ; opt_join_or_resume: - /* nothing */ { Lex->xa_opt=XA_NONE; } - | JOIN_SYM { Lex->xa_opt=XA_JOIN; } - | RESUME_SYM { Lex->xa_opt=XA_RESUME; } - ; + /* nothing */ { Lex->xa_opt=XA_NONE; } + | JOIN_SYM { Lex->xa_opt=XA_JOIN; } + | RESUME_SYM { Lex->xa_opt=XA_RESUME; } + ; opt_one_phase: - /* nothing */ { Lex->xa_opt=XA_NONE; } - | ONE_SYM PHASE_SYM { Lex->xa_opt=XA_ONE_PHASE; } - ; + /* nothing */ { Lex->xa_opt=XA_NONE; } + | ONE_SYM PHASE_SYM { Lex->xa_opt=XA_ONE_PHASE; } + ; opt_suspend: - /* nothing */ { Lex->xa_opt=XA_NONE; } - | SUSPEND_SYM { Lex->xa_opt=XA_SUSPEND; } - opt_migrate - ; + /* nothing */ + { Lex->xa_opt=XA_NONE; } + | SUSPEND_SYM + { Lex->xa_opt=XA_SUSPEND; } + opt_migrate + ; opt_migrate: - /* nothing */ { } - | FOR_SYM MIGRATE_SYM { Lex->xa_opt=XA_FOR_MIGRATE; } - ; + /* nothing */ {} + | FOR_SYM MIGRATE_SYM { Lex->xa_opt=XA_FOR_MIGRATE; } + ; install: - INSTALL_SYM PLUGIN_SYM ident SONAME_SYM TEXT_STRING_sys - { - LEX *lex= Lex; - lex->sql_command= SQLCOM_INSTALL_PLUGIN; - lex->comment= $3; - lex->ident= $5; - }; + INSTALL_SYM PLUGIN_SYM ident SONAME_SYM TEXT_STRING_sys + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_INSTALL_PLUGIN; + lex->comment= $3; + lex->ident= $5; + } + ; uninstall: - UNINSTALL_SYM PLUGIN_SYM ident - { - LEX *lex= Lex; - lex->sql_command= SQLCOM_UNINSTALL_PLUGIN; - lex->comment= $3; - }; + UNINSTALL_SYM PLUGIN_SYM ident + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_UNINSTALL_PLUGIN; + lex->comment= $3; + } + ; + +/** + @} (end of group Parser) +*/ diff --git a/sql/strfunc.cc b/sql/strfunc.cc index 9ffc5fd127f..c03365cfc2b 100644 --- a/sql/strfunc.cc +++ b/sql/strfunc.cc @@ -265,10 +265,8 @@ uint strconvert(CHARSET_INFO *from_cs, const char *from, my_wc_t wc; char *to_start= to; uchar *to_end= (uchar*) to + to_length - 1; - int (*mb_wc)(struct charset_info_st *, my_wc_t *, const uchar *, - const uchar *)= from_cs->cset->mb_wc; - int (*wc_mb)(struct charset_info_st *, my_wc_t, uchar *s, uchar *e)= - to_cs->cset->wc_mb; + my_charset_conv_mb_wc mb_wc= from_cs->cset->mb_wc; + my_charset_conv_wc_mb wc_mb= to_cs->cset->wc_mb; uint error_count= 0; while (1) diff --git a/sql/structs.h b/sql/structs.h index da2339d27f8..09a3c4d7285 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -209,6 +209,11 @@ typedef struct user_conn { char *user; /* Pointer to host part of the key. */ char *host; + /* + The moment of time when per hour counters were reset last time + (i.e. start of "hour" for conn_per_hour, updates, questions counters). + */ + ulonglong reset_utime; /* Total length of the key. */ uint len; /* Current amount of concurrent connections for this account. */ @@ -220,11 +225,6 @@ typedef struct user_conn { uint conn_per_hour, updates, questions; /* Maximum amount of resources which account is allowed to consume. */ USER_RESOURCES user_resources; - /* - The moment of time when per hour counters were reset last time - (i.e. start of "hour" for conn_per_hour, updates, questions counters). - */ - time_t intime; } USER_CONN; /* Bits in form->update */ diff --git a/sql/table.cc b/sql/table.cc index abdb635f899..e2251900f1f 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -19,7 +19,19 @@ #include "mysql_priv.h" #include "sql_trigger.h" #include <m_ctype.h> -#include "md5.h" +#include "my_md5.h" + +/* INFORMATION_SCHEMA name */ +LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")}; + +/* MYSQL_SCHEMA name */ +LEX_STRING MYSQL_SCHEMA_NAME= {C_STRING_WITH_LEN("mysql")}; + +/* GENERAL_LOG name */ +LEX_STRING GENERAL_LOG_NAME= {C_STRING_WITH_LEN("general_log")}; + +/* SLOW_LOG name */ +LEX_STRING SLOW_LOG_NAME= {C_STRING_WITH_LEN("slow_log")}; /* Functions defined in this file */ @@ -31,6 +43,7 @@ static void fix_type_pointers(const char ***array, TYPELIB *point_to_type, uint types, char **names); static uint find_field(Field **fields, uchar *record, uint start, uint length); +inline bool is_system_table_name(const char *name, uint length); /************************************************************************** Object_creation_ctx implementation. @@ -38,11 +51,13 @@ static uint find_field(Field **fields, uchar *record, uint start, uint length); Object_creation_ctx *Object_creation_ctx::set_n_backup(THD *thd) { - Object_creation_ctx *backup_ctx= create_backup_ctx(thd); + Object_creation_ctx *backup_ctx; + DBUG_ENTER("Object_creation_ctx::set_n_backup"); + backup_ctx= create_backup_ctx(thd); change_env(thd); - return backup_ctx; + DBUG_RETURN(backup_ctx); } void Object_creation_ctx::restore_env(THD *thd, Object_creation_ctx *backup_ctx) @@ -71,7 +86,7 @@ Default_object_creation_ctx::Default_object_creation_ctx( { } Object_creation_ctx * -Default_object_creation_ctx::create_backup_ctx(THD *thd) +Default_object_creation_ctx::create_backup_ctx(THD *thd) const { return new Default_object_creation_ctx(thd); } @@ -98,7 +113,7 @@ View_creation_ctx *View_creation_ctx::create(THD *thd) /*************************************************************************/ View_creation_ctx * View_creation_ctx::create(THD *thd, - st_table_list *view) + TABLE_LIST *view) { View_creation_ctx *ctx= new (thd->mem_root) View_creation_ctx(thd); @@ -192,6 +207,49 @@ char *fn_rext(char *name) return name + strlen(name); } +TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name) +{ + DBUG_ASSERT(db != NULL); + DBUG_ASSERT(name != NULL); + + if ((db->length == INFORMATION_SCHEMA_NAME.length) && + (my_strcasecmp(system_charset_info, + INFORMATION_SCHEMA_NAME.str, + db->str) == 0)) + { + return TABLE_CATEGORY_INFORMATION; + } + + if ((db->length == MYSQL_SCHEMA_NAME.length) && + (my_strcasecmp(system_charset_info, + MYSQL_SCHEMA_NAME.str, + db->str) == 0)) + { + if (is_system_table_name(name->str, name->length)) + { + return TABLE_CATEGORY_SYSTEM; + } + + if ((name->length == GENERAL_LOG_NAME.length) && + (my_strcasecmp(system_charset_info, + GENERAL_LOG_NAME.str, + name->str) == 0)) + { + return TABLE_CATEGORY_PERFORMANCE; + } + + if ((name->length == SLOW_LOG_NAME.length) && + (my_strcasecmp(system_charset_info, + SLOW_LOG_NAME.str, + name->str) == 0)) + { + return TABLE_CATEGORY_PERFORMANCE; + } + } + + return TABLE_CATEGORY_USER; +} + /* Allocate a setup TABLE_SHARE structure @@ -297,7 +355,8 @@ void init_tmp_table_share(TABLE_SHARE *share, const char *key, bzero((char*) share, sizeof(*share)); init_sql_alloc(&share->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); - share->tmp_table= INTERNAL_TMP_TABLE; + share->table_category= TABLE_CATEGORY_TEMPORARY; + share->tmp_table= INTERNAL_TMP_TABLE; share->db.str= (char*) key; share->db.length= strlen(key); share->table_cache_key.str= (char*) key; @@ -515,7 +574,15 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags) { if (head[2] == FRM_VER || head[2] == FRM_VER+1 || (head[2] >= FRM_VER+3 && head[2] <= FRM_VER+4)) + { + /* Open view only */ + if (db_flags & OPEN_VIEW_ONLY) + { + error_given= 1; + goto err; + } table_type= 1; + } else { error= 6; // Unkown .frm version @@ -544,28 +611,11 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags) *root_ptr= &share->mem_root; error= open_binary_frm(thd, share, head, file); *root_ptr= old_root; - - 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. - */ - share->system_table= is_system_table_name(share->table_name.str, - share->table_name.length); - if (!share->system_table) - { - 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; } + share->table_category= get_table_category(& share->db, & share->table_name); + if (!error) thd->status_var.opened_shares++; @@ -1351,7 +1401,11 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, the primary key, then we can use any key to find this column */ if (ha_option & HA_PRIMARY_KEY_IN_READ_INDEX) + { field->part_of_key= share->keys_in_use; + if (field->part_of_sortkey.is_set(key)) + field->part_of_sortkey= share->keys_in_use; + } } if (field->key_length() != key_part->length) { @@ -1570,9 +1624,17 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, outparam->keys_in_use_for_query.init(); /* Allocate handler */ - if (!(outparam->file= get_new_handler(share, &outparam->mem_root, - share->db_type()))) - goto err; + outparam->file= 0; + if (!(prgflag & OPEN_FRM_FILE_ONLY)) + { + if (!(outparam->file= get_new_handler(share, &outparam->mem_root, + share->db_type()))) + goto err; + } + else + { + DBUG_ASSERT(!db_stat); + } error= 4; outparam->reginfo.lock_type= TL_UNLOCK; @@ -1692,7 +1754,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, } #ifdef WITH_PARTITION_STORAGE_ENGINE - if (share->partition_info_len) + if (share->partition_info_len && outparam->file) { /* In this execution we must avoid calling thd->change_item_tree since @@ -1820,6 +1882,9 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, bzero((char*) bitmaps, bitmap_size*3); #endif + outparam->no_replicate= outparam->file && + test(outparam->file->ha_table_flags() & + HA_HAS_OWN_BINLOGGING); thd->status_var.opened_tables++; DBUG_RETURN (0); @@ -2427,6 +2492,7 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table) create_info->row_type= share->row_type; create_info->default_table_charset= share->table_charset; create_info->table_charset= 0; + create_info->comment= share->comment; /** @todo MARIA_HACK See hack in mysql_truncate(); when this is properly fixed, the if() below @@ -2866,11 +2932,11 @@ void st_table::reset_item_list(List<Item> *item_list) const calculate md5 of query SYNOPSIS - st_table_list::calc_md5() + TABLE_LIST::calc_md5() buffer buffer for md5 writing */ -void st_table_list::calc_md5(char *buffer) +void TABLE_LIST::calc_md5(char *buffer) { my_MD5_CTX context; uchar digest[16]; @@ -2895,10 +2961,10 @@ void st_table_list::calc_md5(char *buffer) it (it is a kind of optimisation) SYNOPSIS - st_table_list::set_underlying_merge() + TABLE_LIST::set_underlying_merge() */ -void st_table_list::set_underlying_merge() +void TABLE_LIST::set_underlying_merge() { TABLE_LIST *tbl; @@ -2933,7 +2999,7 @@ void st_table_list::set_underlying_merge() setup fields of placeholder of merged VIEW SYNOPSIS - st_table_list::setup_underlying() + TABLE_LIST::setup_underlying() thd - thread handler DESCRIPTION @@ -2946,9 +3012,9 @@ void st_table_list::set_underlying_merge() TRUE - error */ -bool st_table_list::setup_underlying(THD *thd) +bool TABLE_LIST::setup_underlying(THD *thd) { - DBUG_ENTER("st_table_list::setup_underlying"); + DBUG_ENTER("TABLE_LIST::setup_underlying"); if (!field_translation && merge_underlying_list) { @@ -3011,7 +3077,7 @@ bool st_table_list::setup_underlying(THD *thd) Prepare where expression of view SYNOPSIS - st_table_list::prep_where() + TABLE_LIST::prep_where() thd - thread handler conds - condition of this JOIN no_where_clause - do not build WHERE or ON outer qwery do not need it @@ -3025,10 +3091,10 @@ bool st_table_list::setup_underlying(THD *thd) TRUE - error */ -bool st_table_list::prep_where(THD *thd, Item **conds, +bool TABLE_LIST::prep_where(THD *thd, Item **conds, bool no_where_clause) { - DBUG_ENTER("st_table_list::prep_where"); + DBUG_ENTER("TABLE_LIST::prep_where"); for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) { @@ -3128,7 +3194,7 @@ merge_on_conds(THD *thd, TABLE_LIST *table, bool is_cascaded) Prepare check option expression of table SYNOPSIS - st_table_list::prep_check_option() + TABLE_LIST::prep_check_option() thd - thread handler check_opt_type - WITH CHECK OPTION type (VIEW_CHECK_NONE, VIEW_CHECK_LOCAL, VIEW_CHECK_CASCADED) @@ -3143,16 +3209,16 @@ merge_on_conds(THD *thd, TABLE_LIST *table, bool is_cascaded) This method builds check option condition to use it later on every call (usual execution or every SP/PS call). This method have to be called after WHERE preparation - (st_table_list::prep_where) + (TABLE_LIST::prep_where) RETURN FALSE - OK TRUE - error */ -bool st_table_list::prep_check_option(THD *thd, uint8 check_opt_type) +bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type) { - DBUG_ENTER("st_table_list::prep_check_option"); + DBUG_ENTER("TABLE_LIST::prep_check_option"); bool is_cascaded= check_opt_type == VIEW_CHECK_CASCADED; for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) @@ -3211,12 +3277,12 @@ bool st_table_list::prep_check_option(THD *thd, uint8 check_opt_type) Hide errors which show view underlying table information SYNOPSIS - st_table_list::hide_view_error() + TABLE_LIST::hide_view_error() thd thread handler */ -void st_table_list::hide_view_error(THD *thd) +void TABLE_LIST::hide_view_error(THD *thd) { /* Hide "Unknown column" or "Unknown function" error */ if (thd->net.last_errno == ER_BAD_FIELD_ERROR || @@ -3247,7 +3313,7 @@ void st_table_list::hide_view_error(THD *thd) table_to_find (TABLE) SYNOPSIS - st_table_list::find_underlying_table() + TABLE_LIST::find_underlying_table() table_to_find table to find RETURN @@ -3255,7 +3321,7 @@ void st_table_list::hide_view_error(THD *thd) found table reference */ -st_table_list *st_table_list::find_underlying_table(TABLE *table_to_find) +TABLE_LIST *TABLE_LIST::find_underlying_table(TABLE *table_to_find) { /* is this real table and table which we are looking for? */ if (table == table_to_find && merge_underlying_list == 0) @@ -3274,10 +3340,10 @@ st_table_list *st_table_list::find_underlying_table(TABLE *table_to_find) cleunup items belonged to view fields translation table SYNOPSIS - st_table_list::cleanup_items() + TABLE_LIST::cleanup_items() */ -void st_table_list::cleanup_items() +void TABLE_LIST::cleanup_items() { if (!field_translation) return; @@ -3293,7 +3359,7 @@ void st_table_list::cleanup_items() check CHECK OPTION condition SYNOPSIS - st_table_list::view_check_option() + TABLE_LIST::view_check_option() ignore_failure ignore check option fail RETURN @@ -3302,7 +3368,7 @@ void st_table_list::cleanup_items() VIEW_CHECK_SKIP FAILED, but continue */ -int st_table_list::view_check_option(THD *thd, bool ignore_failure) +int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure) { if (check_option && check_option->val_int() == 0) { @@ -3327,7 +3393,7 @@ int st_table_list::view_check_option(THD *thd, bool ignore_failure) table belong to given mask SYNOPSIS - st_table_list::check_single_table() + TABLE_LIST::check_single_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) @@ -3339,9 +3405,9 @@ int st_table_list::view_check_option(THD *thd, bool ignore_failure) TRUE found several tables */ -bool st_table_list::check_single_table(st_table_list **table_arg, +bool TABLE_LIST::check_single_table(TABLE_LIST **table_arg, table_map map, - st_table_list *view_arg) + TABLE_LIST *view_arg) { for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) { @@ -3374,7 +3440,7 @@ bool st_table_list::check_single_table(st_table_list **table_arg, TRUE - out of memory */ -bool st_table_list::set_insert_values(MEM_ROOT *mem_root) +bool TABLE_LIST::set_insert_values(MEM_ROOT *mem_root) { if (table) { @@ -3398,7 +3464,7 @@ bool st_table_list::set_insert_values(MEM_ROOT *mem_root) Test if this is a leaf with respect to name resolution. SYNOPSIS - st_table_list::is_leaf_for_name_resolution() + TABLE_LIST::is_leaf_for_name_resolution() DESCRIPTION A table reference is a leaf with respect to name resolution if @@ -3410,7 +3476,7 @@ bool st_table_list::set_insert_values(MEM_ROOT *mem_root) RETURN TRUE if a leaf, FALSE otherwise. */ -bool st_table_list::is_leaf_for_name_resolution() +bool TABLE_LIST::is_leaf_for_name_resolution() { return (view || is_natural_join || is_join_columns_complete || !nested_join); @@ -3422,7 +3488,7 @@ bool st_table_list::is_leaf_for_name_resolution() respect to name resolution. SYNOPSIS - st_table_list::first_leaf_for_name_resolution() + TABLE_LIST::first_leaf_for_name_resolution() DESCRIPTION Given that 'this' is a nested table reference, recursively walk @@ -3440,7 +3506,7 @@ bool st_table_list::is_leaf_for_name_resolution() else return 'this' */ -TABLE_LIST *st_table_list::first_leaf_for_name_resolution() +TABLE_LIST *TABLE_LIST::first_leaf_for_name_resolution() { TABLE_LIST *cur_table_ref; NESTED_JOIN *cur_nested_join; @@ -3480,7 +3546,7 @@ TABLE_LIST *st_table_list::first_leaf_for_name_resolution() respect to name resolution. SYNOPSIS - st_table_list::last_leaf_for_name_resolution() + TABLE_LIST::last_leaf_for_name_resolution() DESCRIPTION Given that 'this' is a nested table reference, recursively walk @@ -3498,7 +3564,7 @@ TABLE_LIST *st_table_list::first_leaf_for_name_resolution() - else - 'this' */ -TABLE_LIST *st_table_list::last_leaf_for_name_resolution() +TABLE_LIST *TABLE_LIST::last_leaf_for_name_resolution() { TABLE_LIST *cur_table_ref= this; NESTED_JOIN *cur_nested_join; @@ -3540,7 +3606,7 @@ TABLE_LIST *st_table_list::last_leaf_for_name_resolution() want_access Acess which we require */ -void st_table_list::register_want_access(ulong want_access) +void TABLE_LIST::register_want_access(ulong want_access) { /* Remove SHOW_VIEW_ACL, because it will be checked during making view */ want_access&= ~SHOW_VIEW_ACL; @@ -3559,7 +3625,7 @@ void st_table_list::register_want_access(ulong want_access) Load security context information for this view SYNOPSIS - st_table_list::prepare_view_securety_context() + TABLE_LIST::prepare_view_securety_context() thd [in] thread handler RETURN @@ -3568,9 +3634,9 @@ void st_table_list::register_want_access(ulong want_access) */ #ifndef NO_EMBEDDED_ACCESS_CHECKS -bool st_table_list::prepare_view_securety_context(THD *thd) +bool TABLE_LIST::prepare_view_securety_context(THD *thd) { - DBUG_ENTER("st_table_list::prepare_view_securety_context"); + DBUG_ENTER("TABLE_LIST::prepare_view_securety_context"); DBUG_PRINT("enter", ("table: %s", alias)); DBUG_ASSERT(!prelocking_placeholder && view); @@ -3619,17 +3685,17 @@ bool st_table_list::prepare_view_securety_context(THD *thd) Find security context of current view SYNOPSIS - st_table_list::find_view_security_context() + TABLE_LIST::find_view_security_context() thd [in] thread handler */ #ifndef NO_EMBEDDED_ACCESS_CHECKS -Security_context *st_table_list::find_view_security_context(THD *thd) +Security_context *TABLE_LIST::find_view_security_context(THD *thd) { Security_context *sctx; TABLE_LIST *upper_view= this; - DBUG_ENTER("st_table_list::find_view_security_context"); + DBUG_ENTER("TABLE_LIST::find_view_security_context"); DBUG_ASSERT(view); while (upper_view && !upper_view->view_suid) @@ -3658,7 +3724,7 @@ Security_context *st_table_list::find_view_security_context(THD *thd) Prepare security context and load underlying tables priveleges for view SYNOPSIS - st_table_list::prepare_security() + TABLE_LIST::prepare_security() thd [in] thread handler RETURN @@ -3666,11 +3732,11 @@ Security_context *st_table_list::find_view_security_context(THD *thd) TRUE Error */ -bool st_table_list::prepare_security(THD *thd) +bool TABLE_LIST::prepare_security(THD *thd) { List_iterator_fast<TABLE_LIST> tb(*view_tables); TABLE_LIST *tbl; - DBUG_ENTER("st_table_list::prepare_security"); + DBUG_ENTER("TABLE_LIST::prepare_security"); #ifndef NO_EMBEDDED_ACCESS_CHECKS Security_context *save_security_ctx= thd->security_ctx; @@ -4419,10 +4485,10 @@ void st_table::mark_columns_needed_for_insert() Cleanup this table for re-execution. SYNOPSIS - st_table_list::reinit_before_use() + TABLE_LIST::reinit_before_use() */ -void st_table_list::reinit_before_use(THD *thd) +void TABLE_LIST::reinit_before_use(THD *thd) { /* Reset old pointers to TABLEs: they are not valid since the tables @@ -4449,7 +4515,7 @@ void st_table_list::reinit_before_use(THD *thd) Return subselect that contains the FROM list this table is taken from SYNOPSIS - st_table_list::containing_subselect() + TABLE_LIST::containing_subselect() RETURN Subselect item for the subquery that contains the FROM list @@ -4458,7 +4524,7 @@ void st_table_list::reinit_before_use(THD *thd) */ -Item_subselect *st_table_list::containing_subselect() +Item_subselect *TABLE_LIST::containing_subselect() { return (select_lex ? select_lex->master_unit()->item : 0); } @@ -4472,7 +4538,7 @@ Item_subselect *st_table_list::containing_subselect() DESCRIPTION The parser collects the index hints for each table in a "tagged list" - (st_table_list::index_hints). Using the information in this tagged list + (TABLE_LIST::index_hints). Using the information in this tagged list this function sets the members st_table::keys_in_use_for_query, st_table::keys_in_use_for_group_by, st_table::keys_in_use_for_order_by, st_table::force_index and st_table::covering_keys. @@ -4514,11 +4580,11 @@ Item_subselect *st_table_list::containing_subselect() FALSE no errors found TRUE found and reported an error. */ -bool st_table_list::process_index_hints(TABLE *table) +bool TABLE_LIST::process_index_hints(TABLE *tbl) { /* initialize the result variables */ - table->keys_in_use_for_query= table->keys_in_use_for_group_by= - table->keys_in_use_for_order_by= table->s->keys_in_use; + tbl->keys_in_use_for_query= tbl->keys_in_use_for_group_by= + tbl->keys_in_use_for_order_by= tbl->s->keys_in_use; /* index hint list processing */ if (index_hints) @@ -4526,11 +4592,11 @@ bool st_table_list::process_index_hints(TABLE *table) key_map index_join[INDEX_HINT_FORCE + 1]; key_map index_order[INDEX_HINT_FORCE + 1]; key_map index_group[INDEX_HINT_FORCE + 1]; - index_hint *hint; + Index_hint *hint; int type; bool have_empty_use_join= FALSE, have_empty_use_order= FALSE, have_empty_use_group= FALSE; - List_iterator <index_hint> iter(*index_hints); + List_iterator <Index_hint> iter(*index_hints); /* initialize temporary variables used to collect hints of each kind */ for (type= INDEX_HINT_IGNORE; type <= INDEX_HINT_FORCE; type++) @@ -4570,8 +4636,8 @@ bool st_table_list::process_index_hints(TABLE *table) Check if an index with the given name exists and get his offset in the keys bitmask for the table */ - if (table->s->keynames.type_names == 0 || - (pos= find_type(&table->s->keynames, hint->key_name.str, + if (tbl->s->keynames.type_names == 0 || + (pos= find_type(&tbl->s->keynames, hint->key_name.str, hint->key_name.length, 1)) <= 0) { my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), hint->key_name.str, alias); @@ -4607,7 +4673,7 @@ bool st_table_list::process_index_hints(TABLE *table) !index_order[INDEX_HINT_FORCE].is_clear_all() || !index_group[INDEX_HINT_FORCE].is_clear_all()) { - table->force_index= TRUE; + tbl->force_index= TRUE; index_join[INDEX_HINT_USE].merge(index_join[INDEX_HINT_FORCE]); index_order[INDEX_HINT_USE].merge(index_order[INDEX_HINT_FORCE]); index_group[INDEX_HINT_USE].merge(index_group[INDEX_HINT_FORCE]); @@ -4615,20 +4681,20 @@ bool st_table_list::process_index_hints(TABLE *table) /* apply USE INDEX */ if (!index_join[INDEX_HINT_USE].is_clear_all() || have_empty_use_join) - table->keys_in_use_for_query.intersect(index_join[INDEX_HINT_USE]); + tbl->keys_in_use_for_query.intersect(index_join[INDEX_HINT_USE]); if (!index_order[INDEX_HINT_USE].is_clear_all() || have_empty_use_order) - table->keys_in_use_for_order_by.intersect (index_order[INDEX_HINT_USE]); + tbl->keys_in_use_for_order_by.intersect (index_order[INDEX_HINT_USE]); if (!index_group[INDEX_HINT_USE].is_clear_all() || have_empty_use_group) - table->keys_in_use_for_group_by.intersect (index_group[INDEX_HINT_USE]); + tbl->keys_in_use_for_group_by.intersect (index_group[INDEX_HINT_USE]); /* apply IGNORE INDEX */ - table->keys_in_use_for_query.subtract (index_join[INDEX_HINT_IGNORE]); - table->keys_in_use_for_order_by.subtract (index_order[INDEX_HINT_IGNORE]); - table->keys_in_use_for_group_by.subtract (index_group[INDEX_HINT_IGNORE]); + tbl->keys_in_use_for_query.subtract (index_join[INDEX_HINT_IGNORE]); + tbl->keys_in_use_for_order_by.subtract (index_order[INDEX_HINT_IGNORE]); + tbl->keys_in_use_for_group_by.subtract (index_group[INDEX_HINT_IGNORE]); } /* make sure covering_keys don't include indexes disabled with a hint */ - table->covering_keys.intersect(table->keys_in_use_for_query); + tbl->covering_keys.intersect(tbl->keys_in_use_for_query); return 0; } diff --git a/sql/table.h b/sql/table.h index cd3d12614fb..b9c15ea2b6d 100644 --- a/sql/table.h +++ b/sql/table.h @@ -38,7 +38,7 @@ public: static View_creation_ctx *create(THD *thd); static View_creation_ctx *create(THD *thd, - struct st_table_list *view); + TABLE_LIST *view); private: View_creation_ctx(THD *thd) @@ -85,6 +85,15 @@ enum tmp_table_type INTERNAL_TMP_TABLE, SYSTEM_TMP_TABLE }; +/** Event on which trigger is invoked. */ +enum trg_event_type +{ + TRG_EVENT_INSERT= 0, + TRG_EVENT_UPDATE= 1, + TRG_EVENT_DELETE= 2, + TRG_EVENT_MAX +}; + enum frm_type_enum { FRMTYPE_ERROR= 0, @@ -131,6 +140,100 @@ class Field_timestamp; class Field_blob; class Table_triggers_list; +/** + Category of table found in the table share. +*/ +enum enum_table_category +{ + /** + Unknown value. + */ + TABLE_UNKNOWN_CATEGORY=0, + + /** + Temporary table. + The table is visible only in the session. + Therefore, + - FLUSH TABLES WITH READ LOCK + - SET GLOBAL READ_ONLY = ON + do not apply to this table. + Note that LOCK TABLE t FOR READ/WRITE + can be used on temporary tables. + Temporary tables are not part of the table cache. + */ + TABLE_CATEGORY_TEMPORARY=1, + + /** + User table. + These tables do honor: + - LOCK TABLE t FOR READ/WRITE + - FLUSH TABLES WITH READ LOCK + - SET GLOBAL READ_ONLY = ON + User tables are cached in the table cache. + */ + TABLE_CATEGORY_USER=2, + + /** + System table, maintained by the server. + These tables do honor: + - LOCK TABLE t FOR READ/WRITE + - FLUSH TABLES WITH READ LOCK + - SET GLOBAL READ_ONLY = ON + Typically, writes to system tables are performed by + the server implementation, not explicitly be a user. + System tables are cached in the table cache. + */ + TABLE_CATEGORY_SYSTEM=3, + + /** + Information schema tables. + These tables are an interface provided by the system + to inspect the system metadata. + These tables do *not* honor: + - LOCK TABLE t FOR READ/WRITE + - FLUSH TABLES WITH READ LOCK + - SET GLOBAL READ_ONLY = ON + as there is no point in locking explicitely + an INFORMATION_SCHEMA table. + Nothing is directly written to information schema tables. + Note that this value is not used currently, + since information schema tables are not shared, + but implemented as session specific temporary tables. + */ + /* + TODO: Fixing the performance issues of I_S will lead + to I_S tables in the table cache, which should use + this table type. + */ + TABLE_CATEGORY_INFORMATION=4, + + /** + Performance schema tables. + These tables are an interface provided by the system + to inspect the system performance data. + These tables do *not* honor: + - LOCK TABLE t FOR READ/WRITE + - FLUSH TABLES WITH READ LOCK + - SET GLOBAL READ_ONLY = ON + as there is no point in locking explicitely + a PERFORMANCE_SCHEMA table. + An example of PERFORMANCE_SCHEMA tables are: + - mysql.slow_log + - mysql.general_log, + which *are* updated even when there is either + a GLOBAL READ LOCK or a GLOBAL READ_ONLY in effect. + User queries do not write directly to these tables + (there are exceptions for log tables). + The server implementation perform writes. + Performance tables are cached in the table cache. + */ + TABLE_CATEGORY_PERFORMANCE=5 +}; +typedef enum enum_table_category TABLE_CATEGORY; + +TABLE_CATEGORY get_table_category(const LEX_STRING *db, + const LEX_STRING *name); + /* This structure is shared between different table objects. There is one instance of table share per one table in the database. @@ -139,6 +242,10 @@ class Table_triggers_list; typedef struct st_table_share { st_table_share() {} /* Remove gcc warning */ + + /** Category of this table. */ + TABLE_CATEGORY table_category; + /* hash of field names (contains pointers to elements of field array) */ HASH name_hash; /* hash of field names */ MEM_ROOT mem_root; @@ -252,18 +359,6 @@ typedef struct st_table_share */ int cached_row_logging_check; - /* - TRUE if this is a system table like 'mysql.proc', which we want to be - able to open and lock even when we already have some tables open and - locked. To avoid deadlocks we have to put certain restrictions on - locking of this table for writing. FALSE - otherwise. - */ - bool system_table; - /* - This flag is set for the log tables. Used during FLUSH instances to skip - log tables, while closing tables (since logs must be always available) - */ - bool log_table; #ifdef WITH_PARTITION_STORAGE_ENGINE bool auto_partitioned; const char *partition_info; @@ -327,6 +422,16 @@ typedef struct st_table_share set_table_cache_key(key_buff, key_length); } + inline bool honor_global_locks() + { + return ((table_category == TABLE_CATEGORY_USER) + || (table_category == TABLE_CATEGORY_SYSTEM)); + } + + inline bool require_write_privileges() + { + return (table_category == TABLE_CATEGORY_PERFORMANCE); + } } TABLE_SHARE; @@ -388,7 +493,7 @@ struct st_table { /* Table's triggers, 0 if there are no of them */ Table_triggers_list *triggers; - struct st_table_list *pos_in_table_list;/* Element referring to this table */ + TABLE_LIST *pos_in_table_list;/* Element referring to this table */ ORDER *group; const char *alias; /* alias or table name */ uchar *null_flags; @@ -616,6 +721,10 @@ enum enum_schema_tables #define MY_I_S_UNSIGNED 2 +#define SKIP_OPEN_TABLE 0 // do not open table +#define OPEN_FRM_ONLY 1 // open FRM file only +#define OPEN_FULL_TABLE 2 // open FRM,MYD, MYI files + typedef struct st_field_info { const char* field_name; @@ -624,10 +733,11 @@ typedef struct st_field_info int value; uint field_flags; // Field atributes(maybe_null, signed, unsigned etc.) const char* old_name; + uint open_method; } ST_FIELD_INFO; -struct st_table_list; +struct TABLE_LIST; typedef class Item COND; typedef struct st_schema_table @@ -635,16 +745,16 @@ typedef struct st_schema_table const char* table_name; ST_FIELD_INFO *fields_info; /* Create information_schema table */ - TABLE *(*create_table) (THD *thd, struct st_table_list *table_list); + TABLE *(*create_table) (THD *thd, TABLE_LIST *table_list); /* Fill table with data */ - int (*fill_table) (THD *thd, struct st_table_list *tables, COND *cond); + int (*fill_table) (THD *thd, TABLE_LIST *tables, COND *cond); /* Handle fileds for old SHOW */ int (*old_format) (THD *thd, struct st_schema_table *schema_table); - int (*process_table) (THD *thd, struct st_table_list *tables, - TABLE *table, bool res, const char *base_name, - const char *file_name); + int (*process_table) (THD *thd, TABLE_LIST *tables, TABLE *table, + bool res, LEX_STRING *db_name, LEX_STRING *table_name); int idx_field1, idx_field2; bool hidden; + uint i_s_requested_object; /* the object we need to open(TABLE | VIEW) */ } ST_SCHEMA_TABLE; @@ -673,7 +783,7 @@ struct st_lex; class select_union; class TMP_TABLE_PARAM; -Item *create_view_field(THD *thd, st_table_list *view, Item **field_ref, +Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref, const char *name); struct Field_translator @@ -694,7 +804,7 @@ class Natural_join_column: public Sql_alloc public: Field_translator *view_field; /* Column reference of merge view. */ Field *table_field; /* Column reference of table or temp view. */ - st_table_list *table_ref; /* Original base table/view reference. */ + TABLE_LIST *table_ref; /* Original base table/view reference. */ /* True if a common join column of two NATURAL/USING join operands. Notice that when we have a hierarchy of nested NATURAL/USING joins, a column can @@ -704,8 +814,8 @@ public: */ bool is_common; public: - Natural_join_column(Field_translator *field_param, st_table_list *tab); - Natural_join_column(Field *field_param, st_table_list *tab); + Natural_join_column(Field_translator *field_param, TABLE_LIST *tab); + Natural_join_column(Field *field_param, TABLE_LIST *tab); const char *name(); Item *create_item(THD *thd); Field *field(); @@ -747,10 +857,10 @@ public: (TABLE_LIST::join_using_fields != NULL) */ -class index_hint; -typedef struct st_table_list +class Index_hint; +struct TABLE_LIST { - st_table_list() {} /* Remove gcc warning */ + TABLE_LIST() {} /* Remove gcc warning */ /** Prepare TABLE_LIST that consists of one table instance to use in @@ -771,9 +881,9 @@ typedef struct st_table_list views as leaves (unlike 'next_leaf' below). Created at parse time in st_select_lex::add_table_to_list() -> table_list.link_in_list(). */ - struct st_table_list *next_local; + TABLE_LIST *next_local; /* link in a global list of all queries tables */ - struct st_table_list *next_global, **prev_global; + TABLE_LIST *next_global, **prev_global; char *db, *alias, *table_name, *schema_table_name; char *option; /* Used by cache index */ Item *on_expr; /* Used with outer join */ @@ -793,7 +903,7 @@ typedef struct st_table_list 'this' represents a NATURAL or USING join operation. Thus after parsing 'this' is a NATURAL/USING join iff (natural_join != NULL). */ - struct st_table_list *natural_join; + TABLE_LIST *natural_join; /* True if 'this' represents a nested join that is a NATURAL JOIN. For one of the operands of 'this', the member 'natural_join' points @@ -817,9 +927,9 @@ typedef struct st_table_list base tables. All of these TABLE_LIST instances contain a materialized list of columns. The list is local to a subquery. */ - struct st_table_list *next_name_resolution_table; + TABLE_LIST *next_name_resolution_table; /* Index names in a "... JOIN ... USE/IGNORE INDEX ..." clause. */ - List<index_hint> *index_hints; + List<Index_hint> *index_hints; TABLE *table; /* opened table */ uint table_id; /* table id (from binlog) for opened table */ /* @@ -834,7 +944,7 @@ typedef struct st_table_list here it will be reference of first occurrence of t1 to second (as you can see this lists can't be merged) */ - st_table_list *correspondent_table; + TABLE_LIST *correspondent_table; 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; @@ -855,20 +965,20 @@ typedef struct st_table_list does not include the tables of subqueries used in the view. Is set only for merged views. */ - st_table_list *merge_underlying_list; + TABLE_LIST *merge_underlying_list; /* - 0 for base tables - in case of the view it is the list of all (not only underlying tables but also used in subquery ones) tables of the view. */ - List<st_table_list> *view_tables; + List<TABLE_LIST> *view_tables; /* most upper view this table belongs to */ - st_table_list *belong_to_view; + TABLE_LIST *belong_to_view; /* The view directly referencing this table (non-zero only for merged underlying tables of a view). */ - st_table_list *referencing_view; + TABLE_LIST *referencing_view; /* Security context (non-zero only for tables which belong to view with SQL SECURITY DEFINER) @@ -885,7 +995,7 @@ typedef struct st_table_list leaves. Created in setup_tables() -> make_leaves_list(). */ bool allowed_show; - st_table_list *next_leaf; + TABLE_LIST *next_leaf; Item *where; /* VIEW WHERE clause condition */ Item *check_option; /* WITH CHECK OPTION condition */ LEX_STRING select_stmt; /* text of (CREATE/SELECT) statement */ @@ -925,8 +1035,8 @@ typedef struct st_table_list table_map dep_tables; /* tables the table depends on */ table_map on_expr_dep_tables; /* tables on expression depends on */ struct st_nested_join *nested_join; /* if the element is a nested join */ - st_table_list *embedding; /* nested join containing the table */ - List<struct st_table_list> *join_list;/* join list the table belongs to */ + TABLE_LIST *embedding; /* nested join containing the table */ + List<TABLE_LIST> *join_list;/* join list the table belongs to */ bool cacheable_table; /* stop PS caching */ /* used in multi-upd/views privilege check */ bool table_in_first_from_clause; @@ -981,6 +1091,17 @@ typedef struct st_table_list /* End of view definition context. */ + /** + Indicates what triggers we need to pre-load for this TABLE_LIST + when opening an associated TABLE. This is filled after + the parsed tree is created. + */ + uint8 trg_event_map; + + uint i_s_requested_object; + bool has_db_lookup_value; + bool has_table_lookup_value; + uint table_open_method; enum enum_schema_table_state schema_table_state; void calc_md5(char *buffer); void set_underlying_merge(); @@ -993,15 +1114,15 @@ typedef struct st_table_list !table; } void print(THD *thd, String *str); - bool check_single_table(st_table_list **table, table_map map, - st_table_list *view); + bool check_single_table(TABLE_LIST **table, table_map map, + TABLE_LIST *view); bool set_insert_values(MEM_ROOT *mem_root); void hide_view_error(THD *thd); - st_table_list *find_underlying_table(TABLE *table); - st_table_list *first_leaf_for_name_resolution(); - st_table_list *last_leaf_for_name_resolution(); + TABLE_LIST *find_underlying_table(TABLE *table); + TABLE_LIST *first_leaf_for_name_resolution(); + TABLE_LIST *last_leaf_for_name_resolution(); bool is_leaf_for_name_resolution(); - inline st_table_list *top_table() + inline TABLE_LIST *top_table() { return belong_to_view ? belong_to_view : this; } inline bool prepare_check_option(THD *thd) { @@ -1045,7 +1166,7 @@ private: Cleanup for re-execution in a prepared statement or a stored procedure. */ -} TABLE_LIST; +}; class Item; diff --git a/sql/time.cc b/sql/time.cc index c552d085f5e..a6619cf4cee 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -263,11 +263,11 @@ my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, my_bool *in_dst_time_ my_time_t timestamp; *in_dst_time_gap= 0; + thd->time_zone_used= 1; timestamp= thd->variables.time_zone->TIME_to_gmt_sec(t, in_dst_time_gap); if (timestamp) { - thd->time_zone_used= 1; return timestamp; } diff --git a/sql/tztime.cc b/sql/tztime.cc index 0c717dd2ece..9eb38e97827 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -807,19 +807,6 @@ sec_since_epoch(int year, int mon, int mday, int hour, int min ,int sec) SECS_PER_MIN + sec; } - - /* - Works like sec_since_epoch but expects MYSQL_TIME structure as parameter. -*/ - -my_time_t -sec_since_epoch_TIME(MYSQL_TIME *t) -{ - return sec_since_epoch(t->year, t->month, t->day, - t->hour, t->minute, t->second); -} - - /* Converts local time in broken down MYSQL_TIME representation to my_time_t representation. @@ -1425,7 +1412,9 @@ Time_zone_offset::get_name() const static Time_zone_utc tz_UTC; static Time_zone_system tz_SYSTEM; +static Time_zone_offset tz_OFFSET0(0); +Time_zone *my_tz_OFFSET0= &tz_OFFSET0; Time_zone *my_tz_UTC= &tz_UTC; Time_zone *my_tz_SYSTEM= &tz_SYSTEM; @@ -1865,8 +1854,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) */ (void)table->file->ha_index_init(0, 1); - if (table->file->index_read(table->record[0], table->field[0]->ptr, - HA_WHOLE_KEY, HA_READ_KEY_EXACT)) + if (table->file->index_read_map(table->record[0], table->field[0]->ptr, + HA_WHOLE_KEY, HA_READ_KEY_EXACT)) { #ifdef EXTRA_DEBUG /* @@ -1892,8 +1881,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) table->field[0]->store((longlong) tzid, TRUE); (void)table->file->ha_index_init(0, 1); - if (table->file->index_read(table->record[0], table->field[0]->ptr, - HA_WHOLE_KEY, HA_READ_KEY_EXACT)) + if (table->file->index_read_map(table->record[0], table->field[0]->ptr, + HA_WHOLE_KEY, HA_READ_KEY_EXACT)) { sql_print_error("Can't find description of time zone '%u'", tzid); goto end; @@ -1919,8 +1908,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) table->field[0]->store((longlong) tzid, TRUE); (void)table->file->ha_index_init(0, 1); - res= table->file->index_read(table->record[0], table->field[0]->ptr, - (key_part_map)1, HA_READ_KEY_EXACT); + res= table->file->index_read_map(table->record[0], table->field[0]->ptr, + (key_part_map)1, HA_READ_KEY_EXACT); while (!res) { ttid= (uint)table->field[1]->val_int(); @@ -1990,8 +1979,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) table->field[0]->store((longlong) tzid, TRUE); (void)table->file->ha_index_init(0, 1); - res= table->file->index_read(table->record[0], table->field[0]->ptr, - (key_part_map)1, HA_READ_KEY_EXACT); + res= table->file->index_read_map(table->record[0], table->field[0]->ptr, + (key_part_map)1, HA_READ_KEY_EXACT); while (!res) { ttime= (my_time_t)table->field[1]->val_int(); diff --git a/sql/tztime.h b/sql/tztime.h index f7cc7042d79..ddd80b88bf2 100644 --- a/sql/tztime.h +++ b/sql/tztime.h @@ -59,6 +59,7 @@ public: extern Time_zone * my_tz_UTC; extern Time_zone * my_tz_SYSTEM; +extern Time_zone * my_tz_OFFSET0; extern Time_zone * my_tz_find(THD *thd, const String *name); extern my_bool my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap); extern void my_tz_free(); diff --git a/sql/udf_example.c b/sql/udf_example.c index b603464568e..6c07a929b04 100644 --- a/sql/udf_example.c +++ b/sql/udf_example.c @@ -1106,4 +1106,39 @@ char * is_const(UDF_INIT *initid, UDF_ARGS *args __attribute__((unused)), } +my_bool check_const_len_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + if (args->arg_count != 1) + { + strmov(message, "IS_CONST accepts only one argument"); + return 1; + } + if (args->args[0] == 0) + { + initid->ptr= (char*)"Not constant"; + } + else if(strlen(args->args[0]) == args->lengths[0]) + { + initid->ptr= (char*)"Correct length"; + } + else + { + initid->ptr= (char*)"Wrong length"; + } + initid->max_length = 100; + return 0; +} + +char * check_const_len(UDF_INIT *initid, UDF_ARGS *args __attribute__((unused)), + char *result, unsigned long *length, + char *is_null, char *error __attribute__((unused))) +{ + strmov(result, initid->ptr); + *length= strlen(result); + *is_null= 0; + return result; +} + + + #endif /* HAVE_DLOPEN */ diff --git a/sql/udf_example.def b/sql/udf_example.def index 7a87147d7b6..3d569941cc8 100644 --- a/sql/udf_example.def +++ b/sql/udf_example.def @@ -23,3 +23,5 @@ EXPORTS avgcost is_const is_const_init + check_const_len + check_const_len_init diff --git a/sql/uniques.cc b/sql/uniques.cc index 6bcffbd53de..cecb8bd12cf 100644 --- a/sql/uniques.cc +++ b/sql/uniques.cc @@ -360,17 +360,12 @@ Unique::reset() } /* - The comparison function, passed to queue_init() in merge_walk() must + The comparison function, passed to queue_init() in merge_walk() and in + merge_buffers() when the latter is called from Uniques::get() must use comparison function of Uniques::tree, but compare members of struct BUFFPEK. */ -struct BUFFPEK_COMPARE_CONTEXT -{ - qsort_cmp2 key_compare; - void *key_compare_arg; -}; - C_MODE_START static int buffpek_compare(void *arg, uchar *key_ptr1, uchar *key_ptr2) @@ -629,6 +624,10 @@ bool Unique::get(TABLE *table) sort_param.unique_buff= sort_buffer+(sort_param.keys* sort_param.sort_length); + sort_param.compare= (qsort2_cmp) buffpek_compare; + sort_param.cmp_context.key_compare= tree.compare; + sort_param.cmp_context.key_compare_arg= tree.custom_arg; + /* Merge the buffers to one file, removing duplicates */ if (merge_many_buff(&sort_param,sort_buffer,file_ptr,&maxbuffer,&file)) goto err; diff --git a/sql/unireg.cc b/sql/unireg.cc index c74e046b133..da52889fb2f 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -357,6 +357,8 @@ int rea_create_table(THD *thd, const char *path, // Make sure mysql_create_frm din't remove extension DBUG_ASSERT(*fn_rext(frm_name)); + if (thd->variables.keep_files_on_create) + create_info->options|= HA_CREATE_KEEP_FILES; if (file->create_handler_files(path, NULL, CHF_CREATE_FLAG, create_info)) goto err_handler; if (!create_info->frm_only && ha_create_table(thd, path, db, table_name, diff --git a/sql/unireg.h b/sql/unireg.h index 5b73c6e9caa..f0b4a88c7f8 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -85,6 +85,8 @@ #define MAX_FIELDS 4096 /* Limit in the .frm file */ #define MAX_PARTITIONS 1024 +#define MAX_SELECT_NESTING (sizeof(nesting_map)*8-1) + #define MAX_SORT_MEMORY (2048*1024-MALLOC_OVERHEAD) #define MIN_SORT_MEMORY (32*1024-MALLOC_OVERHEAD) @@ -124,7 +126,7 @@ #define SPECIAL_NO_HOST_CACHE 512 /* Don't cache hosts */ #define SPECIAL_SHORT_LOG_FORMAT 1024 #define SPECIAL_SAFE_MODE 2048 -#define SPECIAL_LOG_QUERIES_NOT_USING_INDEXES 4096 /* Log q not using indexes */ +#define SPECIAL_LOG_QUERIES_NOT_USING_INDEXES 4096 /* Obsolete */ /* Extern defines */ #define store_record(A,B) bmove_align((A)->B,(A)->record[0],(size_t) (A)->s->reclength) @@ -153,6 +155,40 @@ #define OPEN_VIEW 8192 /* Allow open on view */ #define OPEN_VIEW_NO_PARSE 16384 /* Open frm only if it's a view, but do not parse view itself */ +/* + This flag is used in function get_all_tables() which fills + I_S tables with data which are retrieved from frm files and storage engine + The flag means that we need to open FRM file only to get necessary data. +*/ +#define OPEN_FRM_FILE_ONLY 32768 +/* + This flag is used in function get_all_tables() which fills + I_S tables with data which are retrieved from frm files and storage engine + The flag means that we need to process tables only to get necessary data. + Views are not processed. +*/ +#define OPEN_TABLE_ONLY OPEN_FRM_FILE_ONLY*2 +/* + This flag is used in function get_all_tables() which fills + I_S tables with data which are retrieved from frm files and storage engine + The flag means that we need to process views only to get necessary data. + Tables are not processed. +*/ +#define OPEN_VIEW_ONLY OPEN_TABLE_ONLY*2 +/* + This flag is used in function get_all_tables() which fills + I_S tables with data which are retrieved from frm files and storage engine. + The flag means that we need to open a view using + open_normal_and_derived_tables() function. +*/ +#define OPEN_VIEW_FULL OPEN_VIEW_ONLY*2 +/* + This flag is used in function get_all_tables() which fills + I_S tables with data which are retrieved from frm files and storage engine. + The flag means that I_S table uses optimization algorithm. +*/ +#define OPTIMIZE_I_S_TABLE OPEN_VIEW_FULL*2 + #define SC_INFO_LENGTH 4 /* Form format constant */ #define TE_INFO_LENGTH 3 #define MTYP_NOEMPTY_BIT 128 |