diff options
Diffstat (limited to 'sql')
111 files changed, 4896 insertions, 1851 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index c63e4ecde9e..9ec686ef9d9 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -52,6 +52,7 @@ ENDIF() INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/sql +${LIBFMT_INCLUDE_DIR} ${PCRE_INCLUDES} ${ZLIB_INCLUDE_DIR} ${SSL_INCLUDE_DIRS} @@ -97,9 +98,9 @@ SET (SQL_SOURCE filesort.cc gstream.cc signal_handler.cc handler.cc - hostname.cc init.cc item.cc item_buff.cc item_cmpfunc.cc - item_create.cc item_func.cc item_geofunc.cc item_row.cc - item_strfunc.cc item_subselect.cc item_sum.cc item_timefunc.cc + hostname.cc init.cc item.cc item_buff.cc item_cmpfunc.cc + item_create.cc item_func.cc item_geofunc.cc item_row.cc + item_strfunc.cc item_subselect.cc item_sum.cc item_timefunc.cc key.cc log.cc lock.cc log_event.cc log_event_server.cc rpl_record.cc rpl_reporting.cc @@ -108,33 +109,33 @@ SET (SQL_SOURCE mysqld.cc net_serv.cc keycaches.cc ../sql-common/client_plugin.c opt_range.cc opt_sum.cc - ../sql-common/pack.c parse_file.cc password.c procedure.cc + ../sql-common/pack.c parse_file.cc password.c procedure.cc protocol.cc records.cc repl_failsafe.cc rpl_filter.cc session_tracker.cc - set_var.cc - slave.cc sp.cc sp_cache.cc sp_head.cc sp_pcontext.cc - sp_rcontext.cc spatial.cc sql_acl.cc sql_analyse.cc sql_base.cc + set_var.cc + slave.cc sp.cc sp_cache.cc sp_head.cc sp_pcontext.cc + sp_rcontext.cc spatial.cc sql_acl.cc sql_analyse.cc sql_base.cc sql_cache.cc sql_class.cc sql_client.cc sql_crypt.cc sql_cursor.cc sql_db.cc sql_delete.cc sql_derived.cc - sql_digest.cc sql_do.cc + sql_digest.cc sql_do.cc sql_error.cc sql_handler.cc sql_get_diagnostics.cc - sql_help.cc sql_insert.cc sql_lex.cc + sql_help.cc sql_insert.cc sql_lex.cc sql_list.cc sql_load.cc sql_manager.cc sql_parse.cc sql_bootstrap.cc - sql_partition.cc sql_plugin.cc sql_prepare.cc sql_rename.cc + sql_partition.cc sql_plugin.cc sql_prepare.cc sql_rename.cc debug_sync.cc debug.cc sql_repl.cc sql_select.cc sql_show.cc sql_state.c group_by_handler.cc derived_handler.cc select_handler.cc sql_statistics.cc sql_string.cc lex_string.h sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc ddl_log.cc ddl_log.h - sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc - sql_time.cc tztime.cc unireg.cc item_xmlfunc.cc + sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc + sql_time.cc tztime.cc unireg.cc item_xmlfunc.cc uniques.cc rpl_tblmap.cc sql_binlog.cc event_scheduler.cc event_data_objects.cc - event_queue.cc event_db_repository.cc - sql_tablespace.cc events.cc ../sql-common/my_user.c + event_queue.cc event_db_repository.cc + events.cc ../sql-common/my_user.c partition_info.cc rpl_utility.cc rpl_utility_server.cc rpl_injector.cc sql_locale.cc rpl_rli.cc rpl_mi.cc sql_servers.cc sql_audit.cc @@ -178,7 +179,7 @@ SET (SQL_SOURCE ${GEN_SOURCES} ${MYSYS_LIBWRAP_SOURCE} ) - + IF ((CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "SunOS" OR WIN32 OR @@ -312,24 +313,24 @@ ENDIF() # On Solaris, some extra effort is required in order to get dtrace probes # from static libraries -DTRACE_INSTRUMENT_STATIC_LIBS(mariadbd +DTRACE_INSTRUMENT_STATIC_LIBS(mariadbd "sql;mysys;mysys_ssl;${MYSQLD_STATIC_PLUGIN_LIBS}") - + SET(WITH_MYSQLD_LDFLAGS "" CACHE STRING "Additional linker flags for mysqld") MARK_AS_ADVANCED(WITH_MYSQLD_LDFLAGS) IF(WITH_MYSQLD_LDFLAGS) GET_TARGET_PROPERTY(MYSQLD_LINK_FLAGS mariadbd LINK_FLAGS) IF(NOT MYSQLD_LINK_FLAGS) - SET(MYSQLD_LINK_FLAGS) - ENDIF() - SET_TARGET_PROPERTIES(mariadbd PROPERTIES LINK_FLAGS + SET(MYSQLD_LINK_FLAGS) + ENDIF() + SET_TARGET_PROPERTIES(mariadbd PROPERTIES LINK_FLAGS "${MYSQLD_LINK_FLAGS} ${WITH_MYSQLD_LDFLAGS}") ENDIF() -# Handle out-of-source build from source package with possibly broken -# bison. Copy bison output to from source to build directory, if not already +# Handle out-of-source build from source package with possibly broken +# bison. Copy bison output to from source to build directory, if not already # there IF (NOT BISON_FOUND) IF (NOT ${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR}) @@ -391,6 +392,10 @@ ADD_CUSTOM_TARGET( ) ADD_DEPENDENCIES(sql GenServerSource) +IF(TARGET libfmt) + ADD_DEPENDENCIES(sql libfmt) +ENDIF() + IF(WIN32 OR HAVE_DLOPEN AND NOT DISABLE_SHARED) ADD_LIBRARY(udf_example MODULE udf_example.c udf_example.def) SET_TARGET_PROPERTIES(udf_example PROPERTIES PREFIX "") @@ -440,9 +445,9 @@ IF(TARGET mariadbd AND (NOT CMAKE_CROSSCOMPILING OR DEFINED CMAKE_CROSSCOMPILING ELSE() SET(ALL_ON_WINDOWS) ENDIF() - ADD_CUSTOM_TARGET(initial_database + ADD_CUSTOM_TARGET(initial_database ${ALL_ON_WINDOWS} - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/initdb.dep + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/initdb.dep ) ENDIF() @@ -451,7 +456,7 @@ IF(WIN32) FILE(TO_NATIVE_PATH ${my_bootstrap_sql} native_outfile) # Create bootstrapper SQL script - ADD_CUSTOM_COMMAND(OUTPUT + ADD_CUSTOM_COMMAND(OUTPUT ${my_bootstrap_sql} COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR}/scripts cmd /c copy mysql_system_tables.sql+mysql_system_tables_data.sql+fill_help_tables.sql+mysql_performance_tables.sql+mysql_test_db.sql+mysql_sys_schema.sql ${native_outfile} @@ -474,7 +479,7 @@ IF(WIN32) DEPENDS comp_sql ${my_bootstrap_sql} ) - MYSQL_ADD_EXECUTABLE(mariadb-install-db + MYSQL_ADD_EXECUTABLE(mariadb-install-db mysql_install_db.cc ${CMAKE_CURRENT_BINARY_DIR}/mysql_bootstrap_sql.c COMPONENT Server diff --git a/sql/backup.cc b/sql/backup.cc index 84c3788a4d4..89cc9b6ee15 100644 --- a/sql/backup.cc +++ b/sql/backup.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2018, 2020, MariaDB Corporation. +/* Copyright (c) 2018, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. @@ -381,6 +381,9 @@ bool backup_end(THD *thd) if (WSREP_NNULL(thd) && thd->wsrep_desynced_backup_stage) { Wsrep_server_state &server_state= Wsrep_server_state::instance(); + THD_STAGE_INFO(thd, stage_waiting_flow); + WSREP_DEBUG("backup_end: waiting for flow control for %s", + wsrep_thd_query(thd)); server_state.resume_and_resync(); thd->wsrep_desynced_backup_stage= false; } @@ -532,7 +535,7 @@ static char *add_id_to_buffer(char *ptr, const LEX_CUSTRING *from) tmp.str= buff; tmp.length= MY_UUID_STRING_LENGTH; - my_uuid2str(from->str, buff); + my_uuid2str(from->str, buff, 1); return add_str_to_buffer(ptr, &tmp); } diff --git a/sql/ddl_log.cc b/sql/ddl_log.cc index 951a7abd95f..418b5f3b5b0 100644 --- a/sql/ddl_log.cc +++ b/sql/ddl_log.cc @@ -77,6 +77,8 @@ #define DDL_LOG_MAGIC_LENGTH 4 /* How many times to try to execute a ddl log entry that causes crashes */ #define DDL_LOG_MAX_RETRY 3 +#define DDL_LOG_RETRY_MASK 0xFF +#define DDL_LOG_RETRY_BITS 8 uchar ddl_log_file_magic[]= { (uchar) 254, (uchar) 254, (uchar) 11, (uchar) 2 }; @@ -155,7 +157,7 @@ mysql_mutex_t LOCK_gdl; #define DDL_LOG_XID_POS 10 /* Used to store unique uuid from the .frm file */ #define DDL_LOG_UUID_POS 18 -/* ID_POS can be used to store something unique, like file size (4 bytes) */ +/* ID_POS can be used to store something unique, like file size (8 bytes) */ #define DDL_LOG_ID_POS DDL_LOG_UUID_POS + MY_UUID_SIZE #define DDL_LOG_END_POS DDL_LOG_ID_POS + 8 @@ -335,7 +337,7 @@ static bool write_ddl_log_file_entry(uint entry_pos) static bool update_phase(uint entry_pos, uchar phase) { DBUG_ENTER("update_phase"); - DBUG_PRINT("enter", ("phase: %d", (int) phase)); + DBUG_PRINT("ddl_log", ("pos: %u phase: %u", entry_pos, (uint) phase)); DBUG_RETURN(mysql_file_pwrite(global_ddl_log.file_id, &phase, 1, global_ddl_log.io_size * entry_pos + @@ -369,6 +371,8 @@ static bool update_next_entry_pos(uint entry_pos, uint next_entry) uchar buff[4]; DBUG_ENTER("update_next_entry_pos"); + DBUG_PRINT("ddl_log", ("pos: %u->%u", entry_pos, next_entry)); + int4store(buff, next_entry); DBUG_RETURN(mysql_file_pwrite(global_ddl_log.file_id, buff, sizeof(buff), global_ddl_log.io_size * entry_pos + @@ -420,6 +424,7 @@ static bool disable_execute_entry(uint entry_pos) { uchar buff[1]; DBUG_ENTER("disable_execute_entry"); + DBUG_PRINT("ddl_log", ("pos: {%u}", entry_pos)); buff[0]= DDL_LOG_IGNORE_ENTRY_CODE; DBUG_RETURN(mysql_file_pwrite(global_ddl_log.file_id, buff, sizeof(buff), @@ -1296,13 +1301,15 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, mysql_mutex_assert_owner(&LOCK_gdl); DBUG_PRINT("ddl_log", - ("entry type: %u action type: %u (%s) phase: %u next: %u " + ("pos: %u=>%u->%u type: %u action: %u (%s) phase: %u " "handler: '%s' name: '%s' from_name: '%s' tmp_name: '%s'", + recovery_state.execute_entry_pos, + ddl_log_entry->entry_pos, + ddl_log_entry->next_entry, (uint) ddl_log_entry->entry_type, (uint) ddl_log_entry->action_type, ddl_log_action_name[ddl_log_entry->action_type], (uint) ddl_log_entry->phase, - ddl_log_entry->next_entry, ddl_log_entry->handler_name.str, ddl_log_entry->name.str, ddl_log_entry->from_name.str, @@ -2470,13 +2477,13 @@ bool ddl_log_write_entry(DDL_LOG_ENTRY *ddl_log_entry, error= FALSE; DBUG_PRINT("ddl_log", - ("entry type: %u action type: %u (%s) phase: %u next: %u " + ("pos: %u->%u action: %u (%s) phase: %u " "handler: '%s' name: '%s' from_name: '%s' tmp_name: '%s'", - (uint) ddl_log_entry->entry_type, + (*active_entry)->entry_pos, + (uint) ddl_log_entry->next_entry, (uint) ddl_log_entry->action_type, ddl_log_action_name[ddl_log_entry->action_type], (uint) ddl_log_entry->phase, - ddl_log_entry->next_entry, ddl_log_entry->handler_name.str, ddl_log_entry->name.str, ddl_log_entry->from_name.str, @@ -2510,6 +2517,7 @@ bool ddl_log_write_entry(DDL_LOG_ENTRY *ddl_log_entry, @param first_entry First entry in linked list of entries to execute. + @param cond_entry Check and don't execute if cond_entry is active @param[in,out] active_entry Entry to execute, 0 = NULL if the entry is written first time and needs to be returned. In this case the entry written @@ -2520,6 +2528,7 @@ bool ddl_log_write_entry(DDL_LOG_ENTRY *ddl_log_entry, */ bool ddl_log_write_execute_entry(uint first_entry, + uint cond_entry, DDL_LOG_MEMORY_ENTRY **active_entry) { uchar *file_entry_buf= global_ddl_log.file_entry_buf; @@ -2536,13 +2545,17 @@ bool ddl_log_write_execute_entry(uint first_entry, file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (uchar)DDL_LOG_EXECUTE_CODE; int4store(file_entry_buf + DDL_LOG_NEXT_ENTRY_POS, first_entry); + int8store(file_entry_buf + DDL_LOG_ID_POS, ((ulonglong)cond_entry << DDL_LOG_RETRY_BITS)); if (!(*active_entry)) { if (ddl_log_get_free_entry(active_entry)) DBUG_RETURN(TRUE); got_free_entry= TRUE; - } + } + DBUG_PRINT("ddl_log", + ("pos: %u=>%u", + (*active_entry)->entry_pos, first_entry)); if (write_ddl_log_file_entry((*active_entry)->entry_pos)) { if (got_free_entry) @@ -2575,6 +2588,7 @@ bool ddl_log_increment_phase(uint entry_pos) { bool error; DBUG_ENTER("ddl_log_increment_phase"); + DBUG_PRINT("ddl_log", ("pos: %u", entry_pos)); mysql_mutex_lock(&LOCK_gdl); error= ddl_log_increment_phase_no_lock(entry_pos); @@ -2754,13 +2768,13 @@ int ddl_log_execute_recovery() recovery_state.xid= ddl_log_entry.xid; /* purecov: begin tested */ - if (ddl_log_entry.unique_id > DDL_LOG_MAX_RETRY) + if ((ddl_log_entry.unique_id & DDL_LOG_RETRY_MASK) > DDL_LOG_MAX_RETRY) { error= -1; continue; } update_unique_id(i, ++ddl_log_entry.unique_id); - if (ddl_log_entry.unique_id > DDL_LOG_MAX_RETRY) + if ((ddl_log_entry.unique_id & DDL_LOG_RETRY_MASK) > DDL_LOG_MAX_RETRY) { sql_print_error("DDL_LOG: Aborting executing entry %u after %llu " "retries", i, ddl_log_entry.unique_id); @@ -2769,6 +2783,15 @@ int ddl_log_execute_recovery() } /* purecov: end tested */ + uint cond_entry= (uint)(ddl_log_entry.unique_id >> DDL_LOG_RETRY_BITS); + + if (cond_entry && is_execute_entry_active(cond_entry)) + { + if (disable_execute_entry(i)) + error= -1; + continue; + } + if (ddl_log_execute_entry_no_lock(thd, ddl_log_entry.next_entry)) { /* Real unpleasant scenario but we have to continue anyway */ @@ -2848,8 +2871,7 @@ void ddl_log_release() Methods for DDL_LOG_STATE */ -static void add_log_entry(DDL_LOG_STATE *state, - DDL_LOG_MEMORY_ENTRY *log_entry) +void ddl_log_add_entry(DDL_LOG_STATE *state, DDL_LOG_MEMORY_ENTRY *log_entry) { log_entry->next_active_log_entry= state->list; state->main_entry= state->list= log_entry; @@ -3028,7 +3050,7 @@ static bool ddl_log_write(DDL_LOG_STATE *ddl_state, ddl_log_release_memory_entry(log_entry); DBUG_RETURN(1); } - add_log_entry(ddl_state, log_entry); + ddl_log_add_entry(ddl_state, log_entry); ddl_state->flags|= ddl_log_entry->flags; // Update cache DBUG_RETURN(0); } @@ -3178,7 +3200,7 @@ static bool ddl_log_drop(THD *thd, DDL_LOG_STATE *ddl_state, } mysql_mutex_unlock(&LOCK_gdl); - add_log_entry(ddl_state, log_entry); + ddl_log_add_entry(ddl_state, log_entry); DBUG_RETURN(0); error: @@ -3478,7 +3500,7 @@ bool ddl_log_store_query(THD *thd, DDL_LOG_STATE *ddl_state, goto err; parent_entry_pos= ddl_state->list->entry_pos; entry_pos= first_entry->entry_pos; - add_log_entry(ddl_state, first_entry); + ddl_log_add_entry(ddl_state, first_entry); while (length) { @@ -3494,7 +3516,7 @@ bool ddl_log_store_query(THD *thd, DDL_LOG_STATE *ddl_state, if (ddl_log_get_free_entry(&next_entry)) goto err; ddl_log_entry.next_entry= next_entry_pos= next_entry->entry_pos; - add_log_entry(ddl_state, next_entry); + ddl_log_add_entry(ddl_state, next_entry); } else { @@ -3526,3 +3548,32 @@ err: mysql_mutex_unlock(&LOCK_gdl); DBUG_RETURN(1); } + + +/* + Log an delete frm file +*/ + +/* + TODO: Partitioning atomic DDL refactoring: this should be replaced with + ddl_log_create_table(). +*/ +bool ddl_log_delete_frm(DDL_LOG_STATE *ddl_state, const char *to_path) +{ + DDL_LOG_ENTRY ddl_log_entry; + DDL_LOG_MEMORY_ENTRY *log_entry; + DBUG_ENTER("ddl_log_delete_frm"); + bzero(&ddl_log_entry, sizeof(ddl_log_entry)); + ddl_log_entry.action_type= DDL_LOG_DELETE_ACTION; + ddl_log_entry.next_entry= ddl_state->list ? ddl_state->list->entry_pos : 0; + + lex_string_set(&ddl_log_entry.handler_name, reg_ext); + lex_string_set(&ddl_log_entry.name, to_path); + + mysql_mutex_assert_owner(&LOCK_gdl); + if (ddl_log_write_entry(&ddl_log_entry, &log_entry)) + DBUG_RETURN(1); + + ddl_log_add_entry(ddl_state, log_entry); + DBUG_RETURN(0); +} diff --git a/sql/ddl_log.h b/sql/ddl_log.h index a2a6af76a77..9960855a813 100644 --- a/sql/ddl_log.h +++ b/sql/ddl_log.h @@ -262,8 +262,14 @@ int ddl_log_execute_recovery(); bool ddl_log_write_entry(DDL_LOG_ENTRY *ddl_log_entry, DDL_LOG_MEMORY_ENTRY **active_entry); +bool ddl_log_write_execute_entry(uint first_entry, uint cond_entry, + DDL_LOG_MEMORY_ENTRY** active_entry); +inline bool ddl_log_write_execute_entry(uint first_entry, - DDL_LOG_MEMORY_ENTRY **active_entry); + DDL_LOG_MEMORY_ENTRY **active_entry) +{ + return ddl_log_write_execute_entry(first_entry, 0, active_entry); +} bool ddl_log_disable_execute_entry(DDL_LOG_MEMORY_ENTRY **active_entry); void ddl_log_complete(DDL_LOG_STATE *ddl_log_state); @@ -279,6 +285,7 @@ void ddl_log_release_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry); bool ddl_log_sync(); bool ddl_log_execute_entry(THD *thd, uint first_entry); +void ddl_log_add_entry(DDL_LOG_STATE *state, DDL_LOG_MEMORY_ENTRY *log_entry); void ddl_log_release_entries(DDL_LOG_STATE *ddl_log_state); bool ddl_log_rename_table(THD *thd, DDL_LOG_STATE *ddl_state, handlerton *hton, @@ -348,5 +355,6 @@ bool ddl_log_alter_table(THD *thd, DDL_LOG_STATE *ddl_state, bool is_renamed); bool ddl_log_store_query(THD *thd, DDL_LOG_STATE *ddl_log_state, const char *query, size_t length); +bool ddl_log_delete_frm(DDL_LOG_STATE *ddl_state, const char *to_path); extern mysql_mutex_t LOCK_gdl; #endif /* DDL_LOG_INCLUDED */ diff --git a/sql/discover.cc b/sql/discover.cc index 4267f97cf59..9b524760b63 100644 --- a/sql/discover.cc +++ b/sql/discover.cc @@ -232,7 +232,7 @@ int extension_based_table_discovery(MY_DIR *dirp, const char *ext_meta, cur++; } advance(from, to, cur, skip); - dirp->number_of_files= (uint)(to - dirp->dir_entry); + dirp->number_of_files= to - dirp->dir_entry; return 0; } diff --git a/sql/encryption.cc b/sql/encryption.cc index 13239b91910..3c7ba2e997b 100644 --- a/sql/encryption.cc +++ b/sql/encryption.cc @@ -109,6 +109,7 @@ int initialize_encryption_plugin(st_plugin_int *plugin) int finalize_encryption_plugin(st_plugin_int *plugin) { + int deinit_status= 0; bool used= plugin_ref_to_int(encryption_manager) == plugin; if (used) @@ -118,18 +119,15 @@ int finalize_encryption_plugin(st_plugin_int *plugin) encryption_handler.encryption_ctx_size_func= zero_size; } - if (plugin && plugin->plugin->deinit && plugin->plugin->deinit(NULL)) - { - DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.", - plugin->name.str)); - } + if (plugin && plugin->plugin->deinit) + deinit_status= plugin->plugin->deinit(NULL); if (used) { plugin_unlock(NULL, encryption_manager); encryption_manager= 0; } - return 0; + return deinit_status; } /****************************************************************** diff --git a/sql/field.cc b/sql/field.cc index 63d39c77d95..b72b0357c23 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -11101,6 +11101,14 @@ Field::set_warning(Sql_condition::enum_warning_level level, uint code, will have table == NULL. */ THD *thd= get_thd(); + + /* + In INPLACE ALTER, server can't know which row has generated + the warning, so the value of current row is supplied by the engine. + */ + if (current_row) + thd->get_stmt_da()->reset_current_row_for_warning(current_row); + if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) { thd->cuted_fields+= cut_increment; diff --git a/sql/gcalc_slicescan.cc b/sql/gcalc_slicescan.cc index 93561f5fe97..e35f9155967 100644 --- a/sql/gcalc_slicescan.cc +++ b/sql/gcalc_slicescan.cc @@ -168,7 +168,7 @@ static void GCALC_DBUG_PRINT_SLICE(const char *header, Gcalc_dyn_list::Gcalc_dyn_list(size_t blk_size, size_t sizeof_item): - m_blk_size(blk_size - ALLOC_ROOT_MIN_BLOCK_SIZE), + m_blk_size(blk_size), m_sizeof_item(ALIGN_SIZE(sizeof_item)), m_points_per_blk((uint)((m_blk_size - PH_DATA_OFFSET) / m_sizeof_item)), m_blk_hook(&m_first_blk), diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index f6c4b95dcbd..f17abed82ff 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -3537,31 +3537,31 @@ bool ha_partition::init_partition_bitmaps() DBUG_ENTER("ha_partition::init_partition_bitmaps"); /* Initialize the bitmap we use to minimize ha_start_bulk_insert calls */ - if (my_bitmap_init(&m_bulk_insert_started, NULL, m_tot_parts + 1, FALSE)) + if (my_bitmap_init(&m_bulk_insert_started, NULL, m_tot_parts + 1)) DBUG_RETURN(true); /* Initialize the bitmap we use to keep track of locked partitions */ - if (my_bitmap_init(&m_locked_partitions, NULL, m_tot_parts, FALSE)) + if (my_bitmap_init(&m_locked_partitions, NULL, m_tot_parts)) DBUG_RETURN(true); /* Initialize the bitmap we use to keep track of partitions which may have something to reset in ha_reset(). */ - if (my_bitmap_init(&m_partitions_to_reset, NULL, m_tot_parts, FALSE)) + if (my_bitmap_init(&m_partitions_to_reset, NULL, m_tot_parts)) DBUG_RETURN(true); /* Initialize the bitmap we use to keep track of partitions which returned HA_ERR_KEY_NOT_FOUND from index_read_map. */ - if (my_bitmap_init(&m_key_not_found_partitions, NULL, m_tot_parts, FALSE)) + if (my_bitmap_init(&m_key_not_found_partitions, NULL, m_tot_parts)) DBUG_RETURN(true); - if (bitmap_init(&m_mrr_used_partitions, NULL, m_tot_parts, TRUE)) + if (my_bitmap_init(&m_mrr_used_partitions, NULL, m_tot_parts)) DBUG_RETURN(true); - if (my_bitmap_init(&m_opened_partitions, NULL, m_tot_parts, FALSE)) + if (my_bitmap_init(&m_opened_partitions, NULL, m_tot_parts)) DBUG_RETURN(true); m_file_sample= NULL; diff --git a/sql/handler.cc b/sql/handler.cc index 2b6d8cad190..9c4c6287950 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -581,6 +581,7 @@ static int hton_drop_table(handlerton *hton, const char *path) int ha_finalize_handlerton(st_plugin_int *plugin) { + int deinit_status= 0; handlerton *hton= (handlerton *)plugin->data; DBUG_ENTER("ha_finalize_handlerton"); @@ -595,18 +596,7 @@ int ha_finalize_handlerton(st_plugin_int *plugin) hton->panic(hton, HA_PANIC_CLOSE); if (plugin->plugin->deinit) - { - /* - Today we have no defined/special behavior for uninstalling - engine plugins. - */ - DBUG_PRINT("info", ("Deinitializing plugin: '%s'", plugin->name.str)); - if (plugin->plugin->deinit(NULL)) - { - DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.", - plugin->name.str)); - } - } + deinit_status= plugin->plugin->deinit(NULL); free_sysvar_table_options(hton); update_discovery_counters(hton, -1); @@ -627,7 +617,7 @@ int ha_finalize_handlerton(st_plugin_int *plugin) my_free(hton); end: - DBUG_RETURN(0); + DBUG_RETURN(deinit_status); } @@ -1746,6 +1736,13 @@ int ha_commit_trans(THD *thd, bool all) if (ha_info->ht()->prepare_commit_versioned) { trx_end_id= ha_info->ht()->prepare_commit_versioned(thd, &trx_start_id); + + if (trx_end_id == ULONGLONG_MAX) + { + my_error(ER_ERROR_DURING_COMMIT, MYF(0), 1); + goto err; + } + if (trx_end_id) break; // FIXME: use a common ID for cross-engine transactions } @@ -8229,7 +8226,7 @@ static Create_field *vers_init_sys_field(THD *thd, const char *field_name, int f f->set_handler(&type_handler_timestamp2); f->length= MAX_DATETIME_PRECISION; } - f->invisible= DBUG_EVALUATE_IF("sysvers_show", VISIBLE, INVISIBLE_SYSTEM); + f->invisible= DBUG_IF("sysvers_show") ? VISIBLE : INVISIBLE_SYSTEM; if (f->check(thd)) return NULL; @@ -8374,7 +8371,7 @@ bool Vers_parse_info::fix_alter_info(THD *thd, Alter_info *alter_info, if (!need_check(alter_info) && !share->versioned) return false; - if (DBUG_EVALUATE_IF("sysvers_force", 0, share->tmp_table)) + if (!DBUG_IF("sysvers_force") && share->tmp_table) { my_error(ER_VERS_NOT_SUPPORTED, MYF(0), "CREATE TEMPORARY TABLE"); return true; diff --git a/sql/handler.h b/sql/handler.h index 14a306f7774..fe61666bf20 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -829,7 +829,9 @@ typedef bool Log_func(THD*, TABLE*, bool, const uchar*, const uchar*); // Set by Sql_cmd_alter_table_truncate_partition::execute() #define ALTER_PARTITION_TRUNCATE (1ULL << 11) // Set for REORGANIZE PARTITION -#define ALTER_PARTITION_TABLE_REORG (1ULL << 12) +#define ALTER_PARTITION_TABLE_REORG (1ULL << 12) +#define ALTER_PARTITION_CONVERT_IN (1ULL << 13) +#define ALTER_PARTITION_CONVERT_OUT (1ULL << 14) /* This is master database for most of system tables. However there @@ -974,39 +976,6 @@ struct xid_recovery_member #define MIN_XID_LIST_SIZE 128 #define MAX_XID_LIST_SIZE (1024*128) -/* - These structures are used to pass information from a set of SQL commands - on add/drop/change tablespace definitions to the proper hton. -*/ -#define UNDEF_NODEGROUP 65535 -enum ts_command_type -{ - TS_CMD_NOT_DEFINED = -1, - CREATE_TABLESPACE = 0, - ALTER_TABLESPACE = 1, - CREATE_LOGFILE_GROUP = 2, - ALTER_LOGFILE_GROUP = 3, - DROP_TABLESPACE = 4, - DROP_LOGFILE_GROUP = 5, - CHANGE_FILE_TABLESPACE = 6, - ALTER_ACCESS_MODE_TABLESPACE = 7 -}; - -enum ts_alter_tablespace_type -{ - TS_ALTER_TABLESPACE_TYPE_NOT_DEFINED = -1, - ALTER_TABLESPACE_ADD_FILE = 1, - ALTER_TABLESPACE_DROP_FILE = 2 -}; - -enum tablespace_access_mode -{ - TS_NOT_DEFINED= -1, - TS_READ_ONLY = 0, - TS_READ_WRITE = 1, - TS_NOT_ACCESSIBLE = 2 -}; - /* Statistics about batch operations like bulk_insert */ struct ha_copy_info { @@ -1017,50 +986,6 @@ struct ha_copy_info ha_rows updated; }; -struct handlerton; -class st_alter_tablespace : public Sql_alloc -{ - public: - const char *tablespace_name; - const char *logfile_group_name; - enum ts_command_type ts_cmd_type; - enum ts_alter_tablespace_type ts_alter_tablespace_type; - const char *data_file_name; - const char *undo_file_name; - const char *redo_file_name; - ulonglong extent_size; - ulonglong undo_buffer_size; - ulonglong redo_buffer_size; - ulonglong initial_size; - ulonglong autoextend_size; - ulonglong max_size; - uint nodegroup_id; - handlerton *storage_engine; - bool wait_until_completed; - const char *ts_comment; - enum tablespace_access_mode ts_access_mode; - st_alter_tablespace() - { - tablespace_name= NULL; - logfile_group_name= "DEFAULT_LG"; //Default log file group - ts_cmd_type= TS_CMD_NOT_DEFINED; - data_file_name= NULL; - undo_file_name= NULL; - redo_file_name= NULL; - extent_size= 1024*1024; //Default 1 MByte - undo_buffer_size= 8*1024*1024; //Default 8 MByte - redo_buffer_size= 8*1024*1024; //Default 8 MByte - initial_size= 128*1024*1024; //Default 128 MByte - autoextend_size= 0; //No autoextension as default - max_size= 0; //Max size == initial size => no extension - storage_engine= NULL; - nodegroup_id= UNDEF_NODEGROUP; - wait_until_completed= TRUE; - ts_comment= NULL; - ts_access_mode= TS_NOT_DEFINED; - } -}; - /* The handler for a table type. Will be included in the TABLE structure */ struct TABLE; @@ -1124,6 +1049,7 @@ typedef bool (stat_print_fn)(THD *thd, const char *type, size_t type_len, enum ha_stat_type { HA_ENGINE_STATUS, HA_ENGINE_LOGS, HA_ENGINE_MUTEX }; extern MYSQL_PLUGIN_IMPORT st_plugin_int *hton2plugin[MAX_HA]; +struct handlerton; #define view_pseudo_hton ((handlerton *)1) /* Transaction log maintains type definitions */ @@ -1554,8 +1480,7 @@ struct handlerton bool (*show_status)(handlerton *hton, THD *thd, stat_print_fn *print, enum ha_stat_type stat); uint (*partition_flags)(); alter_table_operations (*alter_table_flags)(alter_table_operations flags); - int (*alter_tablespace)(handlerton *hton, THD *thd, st_alter_tablespace *ts_info); - int (*fill_is_table)(handlerton *hton, THD *thd, TABLE_LIST *tables, + int (*fill_is_table)(handlerton *hton, THD *thd, TABLE_LIST *tables, class Item *cond, enum enum_schema_tables); uint32 flags; /* global handler flags */ diff --git a/sql/hostname.cc b/sql/hostname.cc index 7b07ab620a6..35948db3d23 100644 --- a/sql/hostname.cc +++ b/sql/hostname.cc @@ -191,7 +191,7 @@ Host_entry *hostname_cache_first() static inline Host_entry *hostname_cache_search(const char *ip_key) { - return hostname_cache->search((uchar *) ip_key, 0); + return hostname_cache->search((uchar *) ip_key, HOST_ENTRY_KEY_SIZE); } static void add_hostname_impl(const char *ip_key, const char *hostname, diff --git a/sql/item.cc b/sql/item.cc index 125d1848506..e0456edfee0 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -55,8 +55,8 @@ const char *item_empty_name=""; const char *item_used_name= "\0"; static int save_field_in_field(Field *, bool *, Field *, bool); -const Item_bool_static Item_false("FALSE", 0); -const Item_bool_static Item_true("TRUE", 1); +Item_bool_static *Item_false; +Item_bool_static *Item_true; /** Compare two Items for List<Item>::add_unique() @@ -446,7 +446,7 @@ Item::Item(THD *thd): Item::Item(): name(null_clex_str), orig_name(0), is_expensive_cache(-1) { - DBUG_ASSERT(my_progname == NULL); // before main() + DBUG_ASSERT(!mysqld_server_started); // Created early base_flags= item_base_t::FIXED; with_flags= item_with_t::NONE; null_value= 0; diff --git a/sql/item.h b/sql/item.h index 468b9932f02..636ec47b43e 100644 --- a/sql/item.h +++ b/sql/item.h @@ -769,8 +769,8 @@ enum class item_base_t : item_flags_t FIXED= (1<<2), // Was fixed with fix_fields(). IS_EXPLICIT_NAME= (1<<3), // The name of this Item was set by the user // (or was auto generated otherwise) - IS_IN_WITH_CYCLE= (1<<4) // This item is in CYCLE clause - // of WITH. + IS_IN_WITH_CYCLE= (1<<4), // This item is in CYCLE clause of WITH. + AT_TOP_LEVEL= (1<<5) // At top (AND) level of item tree }; @@ -1319,6 +1319,25 @@ public: { set_maybe_null(maybe_null_arg); } + /* + Mark the item that it is a top level item, or part of a top level AND item, + for WHERE and ON clauses: + Example: ... WHERE a=5 AND b=6; Both a=5 and b=6 are top level items + + This is used to indicate that there is no distinction between if the + value of the item is FALSE or NULL.. + This enables Item_cond_and and subquery related items to do special + "top level" optimizations. + */ + virtual void top_level_item() + { + base_flags|= item_base_t::AT_TOP_LEVEL; + } + /* + Return TRUE if this item of top WHERE level (AND/OR) + */ + bool is_top_level_item() const + { return (bool) (base_flags & item_base_t::AT_TOP_LEVEL); } void set_typelib(const TYPELIB *typelib) override { @@ -2035,25 +2054,6 @@ public: { return type_handler()->Item_update_null_value(this); } - - /* - Inform the item that there will be no distinction between its result - being FALSE or NULL. - - NOTE - This function will be called for eg. Items that are top-level AND-parts - of the WHERE clause. Items implementing this function (currently - Item_cond_and and subquery-related item) enable special optimizations - when they are "top level". - */ - virtual void top_level_item() {} - /* - Return TRUE if it is item of top WHERE level (AND/OR) and it is - important, return FALSE if it not important (we can not use to simplify - calculations) or not top level - */ - virtual bool is_top_level_item() const - { return FALSE; /* not important */} /* return IN/ALL/ANY subquery or NULL */ @@ -2647,7 +2647,7 @@ public: bool depends_only_on(table_map view_map) { return marker & MARKER_FULL_EXTRACTION; } - int get_extraction_flag() + int get_extraction_flag() const { return marker & MARKER_EXTRACTION_MASK; } void set_extraction_flag(int16 flags) { @@ -4430,11 +4430,16 @@ public: Item_bool_static(const char *str_arg, longlong i): Item_bool(str_arg, i) {}; + /* Don't mark static items as top level item */ + virtual void top_level_item() override {} void set_join_tab_idx(uint8 join_tab_idx_arg) override { DBUG_ASSERT(0); } + + void cleanup() override {} }; -extern const Item_bool_static Item_false, Item_true; +/* The following variablese are stored in a read only segment */ +extern Item_bool_static *Item_false, *Item_true; class Item_uint :public Item_int { diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 5f21da17d8b..0d22465af6b 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1065,14 +1065,14 @@ int Arg_comparator::compare_row() // NULL was compared switch (((Item_func*)owner)->functype()) { case Item_func::NE_FUNC: - break; // NE never aborts on NULL even if abort_on_null is set + break; // NE never aborts on NULL case Item_func::LT_FUNC: case Item_func::LE_FUNC: case Item_func::GT_FUNC: case Item_func::GE_FUNC: return -1; // <, <=, > and >= always fail on NULL case Item_func::EQ_FUNC: - if (((Item_func_eq*)owner)->abort_on_null) + if (owner->is_top_level_item()) return -1; // We do not need correct NULL returning break; default: @@ -1189,12 +1189,6 @@ longlong Item_func_truth::val_int() } -bool Item_in_optimizer::is_top_level_item() const -{ - return args[1]->is_top_level_item(); -} - - void Item_in_optimizer::fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge) { @@ -1379,7 +1373,8 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref) } base_flags|= (item_base_t::FIXED | - (args[1]->base_flags & item_base_t::MAYBE_NULL)); + (args[1]->base_flags & (item_base_t::MAYBE_NULL | + item_base_t::AT_TOP_LEVEL))); with_flags|= (item_with_t::SUBQUERY | args[1]->with_flags | (args[0]->with_flags & @@ -2065,7 +2060,7 @@ bool Item_func_between::eval_not_null_tables(void *opt_arg) return 1; /* not_null_tables_cache == union(T1(e),T1(e1),T1(e2)) */ - if (pred_level && !negated) + if (is_top_level_item() && !negated) return 0; /* not_null_tables_cache == union(T1(e), intersection(T1(e1),T1(e2))) */ @@ -2467,6 +2462,10 @@ bool Item_func_if::fix_fields(THD *thd, Item **ref) { DBUG_ASSERT(fixed() == 0); + /* + Mark that we don't care if args[0] is NULL or FALSE, we regard both cases as + false. + */ args[0]->top_level_item(); if (Item_func::fix_fields(thd, ref)) @@ -4342,7 +4341,7 @@ Item_func_in::eval_not_null_tables(void *opt_arg) return 1; /* not_null_tables_cache == union(T1(e),union(T1(ei))) */ - if (pred_level && negated) + if (is_top_level_item() && negated) return 0; /* not_null_tables_cache = union(T1(e),intersection(T1(ei))) */ @@ -4796,9 +4795,10 @@ bool Item_func_bit_and::fix_length_and_dec() Item_cond::Item_cond(THD *thd, Item_cond *item) :Item_bool_func(thd, item), - abort_on_null(item->abort_on_null), and_tables_cache(item->and_tables_cache) { + base_flags|= (item->base_flags & item_base_t::AT_TOP_LEVEL); + /* item->list will be copied by copy_andor_arguments() call */ @@ -4806,7 +4806,7 @@ Item_cond::Item_cond(THD *thd, Item_cond *item) Item_cond::Item_cond(THD *thd, Item *i1, Item *i2): - Item_bool_func(thd), abort_on_null(0) + Item_bool_func(thd) { list.push_back(i1, thd->mem_root); list.push_back(i2, thd->mem_root); @@ -4874,7 +4874,7 @@ Item_cond::fix_fields(THD *thd, Item **ref) ((Item_cond*) item)->list.empty(); item= *li.ref(); // new current item } - if (abort_on_null) + if (is_top_level_item()) item->top_level_item(); /* @@ -4901,7 +4901,7 @@ Item_cond::fix_fields(THD *thd, Item **ref) if (item->can_eval_in_optimize() && !item->with_sp_var() && !cond_has_datetime_is_null(item)) { - if (item->eval_const_cond() == is_and_cond && top_level()) + if (item->eval_const_cond() == is_and_cond && is_top_level_item()) { /* a. This is "... AND true_cond AND ..." @@ -4958,7 +4958,7 @@ Item_cond::eval_not_null_tables(void *opt_arg) if (item->can_eval_in_optimize() && !item->with_sp_var() && !cond_has_datetime_is_null(item)) { - if (item->eval_const_cond() == is_and_cond && top_level()) + if (item->eval_const_cond() == is_and_cond && is_top_level_item()) { /* a. This is "... AND true_cond AND ..." @@ -5393,17 +5393,18 @@ void Item_cond_and::mark_as_condition_AND_part(TABLE_LIST *embedding) Evaluation of AND(expr, expr, expr ...). @note - abort_if_null is set for AND expressions for which we don't care if the - result is NULL or 0. This is set for: + There are AND expressions for which we don't care if the + result is NULL or 0. This is the case for: - WHERE clause - HAVING clause - IF(expression) + For these we mark them as "top_level_items" @retval 1 If all expressions are true @retval - 0 If all expressions are false or if we find a NULL expression and - 'abort_on_null' is set. + 0 If any of the expressions are false or if we find a NULL expression and + this is a top_level_item. @retval NULL if all expression are either 1 or NULL */ @@ -5419,8 +5420,8 @@ longlong Item_cond_and::val_int() { if (!item->val_bool()) { - if (abort_on_null || !(null_value= item->null_value)) - return 0; // return FALSE + if (is_top_level_item() || !(null_value= item->null_value)) + return 0; } } return null_value ? 0 : 1; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index bc7441c2530..3767c2172e8 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -411,7 +411,6 @@ public: void set_join_tab_idx(uint8 join_tab_idx_arg) override { args[1]->set_join_tab_idx(join_tab_idx_arg); } void get_cache_parameters(List<Item> ¶meters) override; - bool is_top_level_item() const override; bool eval_not_null_tables(void *opt_arg) override; bool find_not_null_fields(table_map allowed) override; void fix_after_pullout(st_select_lex *new_parent, Item **ref, @@ -631,12 +630,8 @@ public: class Item_func_not :public Item_bool_func { - bool abort_on_null; public: - Item_func_not(THD *thd, Item *a): - Item_bool_func(thd, a), abort_on_null(FALSE) {} - void top_level_item() override { abort_on_null= 1; } - bool is_top_level_item() const override { return abort_on_null; } + Item_func_not(THD *thd, Item *a): Item_bool_func(thd, a) {} longlong val_int() override; enum Functype functype() const override { return NOT_FUNC; } LEX_CSTRING func_name_cstring() const override @@ -755,11 +750,10 @@ public: class Item_func_eq :public Item_bool_rowready_func2 { - bool abort_on_null; public: Item_func_eq(THD *thd, Item *a, Item *b): Item_bool_rowready_func2(thd, a, b), - abort_on_null(false), in_equality_no(UINT_MAX) + in_equality_no(UINT_MAX) {} longlong val_int() override; enum Functype functype() const override { return EQ_FUNC; } @@ -770,7 +764,6 @@ public: static LEX_CSTRING name= {STRING_WITH_LEN("=") }; return name; } - void top_level_item() override { abort_on_null= true; } Item *negated_item(THD *thd) override; COND *build_equal_items(THD *thd, COND_EQUAL *inherited, bool link_item_fields, @@ -956,15 +949,12 @@ protected: DTCollation cmp_collation; public: bool negated; /* <=> the item represents NOT <func> */ - bool pred_level; /* <=> [NOT] <func> is used on a predicate level */ public: Item_func_opt_neg(THD *thd, Item *a, Item *b, Item *c): - Item_bool_func(thd, a, b, c), negated(0), pred_level(0) {} + Item_bool_func(thd, a, b, c), negated(0) {} Item_func_opt_neg(THD *thd, List<Item> &list): - Item_bool_func(thd, list), negated(0), pred_level(0) {} + Item_bool_func(thd, list), negated(0) {} public: - void top_level_item() override { pred_level= 1; } - bool is_top_level_item() const override { return pred_level; } Item *neg_transformer(THD *thd) override { negated= !negated; @@ -2800,11 +2790,9 @@ public: class Item_func_isnotnull :public Item_func_null_predicate { - bool abort_on_null; public: Item_func_isnotnull(THD *thd, Item *a): - Item_func_null_predicate(thd, a), abort_on_null(0) - { } + Item_func_null_predicate(thd, a) {} longlong val_int() override; enum Functype functype() const override { return ISNOTNULL_FUNC; } LEX_CSTRING func_name_cstring() const override @@ -2814,10 +2802,9 @@ public: } enum precedence precedence() const override { return CMP_PRECEDENCE; } table_map not_null_tables() const override - { return abort_on_null ? not_null_tables_cache : 0; } + { return is_top_level_item() ? not_null_tables_cache : 0; } Item *neg_transformer(THD *thd) override; void print(String *str, enum_query_type query_type) override; - void top_level_item() override { abort_on_null=1; } Item *get_copy(THD *thd) override { return get_item_copy<Item_func_isnotnull>(thd, this); } }; @@ -3128,17 +3115,19 @@ class Item_cond :public Item_bool_func { protected: List<Item> list; - bool abort_on_null; table_map and_tables_cache; public: - /* Item_cond() is only used to create top level items */ - Item_cond(THD *thd): Item_bool_func(thd), abort_on_null(1) - { const_item_cache=0; } + Item_cond(THD *thd): Item_bool_func(thd) + { + /* Item_cond() is only used to create top level items */ + top_level_item(); + const_item_cache=0; + } Item_cond(THD *thd, Item *i1, Item *i2); Item_cond(THD *thd, Item_cond *item); Item_cond(THD *thd, List<Item> &nlist): - Item_bool_func(thd), list(nlist), abort_on_null(0) {} + Item_bool_func(thd), list(nlist) {} bool add(Item *item, MEM_ROOT *root) { DBUG_ASSERT(item); @@ -3185,8 +3174,6 @@ public: List<Item> &fields, uint flags) override; friend int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, COND **conds); - void top_level_item() override { abort_on_null=1; } - bool top_level() { return abort_on_null; } void copy_andor_arguments(THD *thd, Item_cond *item); bool walk(Item_processor processor, bool walk_subquery, void *arg) override; Item *transform(THD *thd, Item_transformer transformer, uchar *arg) override; @@ -3542,7 +3529,7 @@ public: } enum precedence precedence() const override { return AND_PRECEDENCE; } table_map not_null_tables() const override - { return abort_on_null ? not_null_tables_cache: and_tables_cache; } + { return is_top_level_item() ? not_null_tables_cache: and_tables_cache; } Item *copy_andor_structure(THD *thd) override; Item *neg_transformer(THD *thd) override; void mark_as_condition_AND_part(TABLE_LIST *embedding) override; diff --git a/sql/item_create.cc b/sql/item_create.cc index c80cf7b03d7..1aa7d02e76b 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -902,6 +902,32 @@ protected: }; +class Create_func_json_normalize : public Create_func_arg1 +{ +public: + virtual Item *create_1_arg(THD *thd, Item *arg1); + + static Create_func_json_normalize s_singleton; + +protected: + Create_func_json_normalize() {} + virtual ~Create_func_json_normalize() {} +}; + + +class Create_func_json_equals : public Create_func_arg2 +{ +public: + virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_json_equals s_singleton; + +protected: + Create_func_json_equals() {} + virtual ~Create_func_json_equals() {} +}; + + class Create_func_json_exists : public Create_func_arg2 { public: @@ -1596,6 +1622,15 @@ protected: virtual ~Create_func_name_const() {} }; +class Create_func_natural_sort_key : public Create_func_arg1 +{ +public: + virtual Item *create_1_arg(THD *thd, Item *arg1) override; + static Create_func_natural_sort_key s_singleton; +protected: + Create_func_natural_sort_key() {} + virtual ~Create_func_natural_sort_key() {} +}; class Create_func_nullif : public Create_func_arg2 { @@ -1896,6 +1931,16 @@ protected: virtual ~Create_func_sec_to_time() {} }; +class Create_func_sformat : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_CSTRING *name, + List<Item> *item_list); + static Create_func_sformat s_singleton; +protected: + Create_func_sformat() {} + virtual ~Create_func_sformat() {} +}; class Create_func_sha : public Create_func_arg1 { @@ -2235,30 +2280,6 @@ protected: }; -class Create_func_uuid : public Create_func_arg0 -{ -public: - virtual Item *create_builder(THD *thd); - - static Create_func_uuid s_singleton; - -protected: - Create_func_uuid() {} - virtual ~Create_func_uuid() {} -}; - -class Create_func_sys_guid : public Create_func_arg0 -{ -public: - virtual Item *create_builder(THD *thd); - - static Create_func_sys_guid s_singleton; - -protected: - Create_func_sys_guid() {} - virtual ~Create_func_sys_guid() {} -}; - class Create_func_uuid_short : public Create_func_arg0 { public: @@ -3596,6 +3617,25 @@ Create_func_isnull::create_1_arg(THD *thd, Item *arg1) return new (thd->mem_root) Item_func_isnull(thd, arg1); } +Create_func_json_normalize Create_func_json_normalize::s_singleton; + +Item* +Create_func_json_normalize::create_1_arg(THD *thd, Item *arg1) +{ + status_var_increment(thd->status_var.feature_json); + return new (thd->mem_root) Item_func_json_normalize(thd, arg1); +} + + +Create_func_json_equals Create_func_json_equals::s_singleton; + +Item* +Create_func_json_equals::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + status_var_increment(thd->status_var.feature_json); + return new (thd->mem_root) Item_func_json_equals(thd, arg1, arg2); +} + Create_func_json_exists Create_func_json_exists::s_singleton; @@ -4597,6 +4637,12 @@ Create_func_md5::create_1_arg(THD *thd, Item *arg1) return new (thd->mem_root) Item_func_md5(thd, arg1); } +Create_func_natural_sort_key Create_func_natural_sort_key::s_singleton; + +Item *Create_func_natural_sort_key::create_1_arg(THD *thd, Item* arg1) +{ + return new (thd->mem_root) Item_func_natural_sort_key(thd, arg1); +} Create_func_monthname Create_func_monthname::s_singleton; @@ -4965,6 +5011,26 @@ Create_func_sec_to_time::create_1_arg(THD *thd, Item *arg1) return new (thd->mem_root) Item_func_sec_to_time(thd, arg1); } +Create_func_sformat Create_func_sformat::s_singleton; + +Item* +Create_func_sformat::create_native(THD *thd, LEX_CSTRING *name, + List<Item> *item_list) +{ + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + if (unlikely(arg_count < 1)) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); + return NULL; + } + + return new (thd->mem_root) Item_func_sformat(thd, *item_list); +} + Create_func_sha Create_func_sha::s_singleton; @@ -5283,29 +5349,6 @@ Create_func_unix_timestamp::create_native(THD *thd, LEX_CSTRING *name, } -Create_func_uuid Create_func_uuid::s_singleton; - -Item* -Create_func_uuid::create_builder(THD *thd) -{ - DBUG_ENTER("Create_func_uuid::create"); - thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); - thd->lex->safe_to_cache_query= 0; - DBUG_RETURN(new (thd->mem_root) Item_func_uuid(thd, 0)); -} - -Create_func_sys_guid Create_func_sys_guid::s_singleton; - -Item* -Create_func_sys_guid::create_builder(THD *thd) -{ - DBUG_ENTER("Create_func_sys_guid::create"); - thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); - thd->lex->safe_to_cache_query= 0; - DBUG_RETURN(new (thd->mem_root) Item_func_uuid(thd, 1)); -} - - Create_func_uuid_short Create_func_uuid_short::s_singleton; Item* @@ -5552,6 +5595,7 @@ Native_func_registry func_array[] = { { STRING_WITH_LEN("JSON_CONTAINS_PATH") }, BUILDER(Create_func_json_contains_path)}, { { STRING_WITH_LEN("JSON_DEPTH") }, BUILDER(Create_func_json_depth)}, { { STRING_WITH_LEN("JSON_DETAILED") }, BUILDER(Create_func_json_detailed)}, + { { STRING_WITH_LEN("JSON_EQUALS") }, BUILDER(Create_func_json_equals)}, { { STRING_WITH_LEN("JSON_EXISTS") }, BUILDER(Create_func_json_exists)}, { { STRING_WITH_LEN("JSON_EXTRACT") }, BUILDER(Create_func_json_extract)}, { { STRING_WITH_LEN("JSON_INSERT") }, BUILDER(Create_func_json_insert)}, @@ -5561,6 +5605,7 @@ Native_func_registry func_array[] = { { STRING_WITH_LEN("JSON_MERGE") }, BUILDER(Create_func_json_merge)}, { { STRING_WITH_LEN("JSON_MERGE_PATCH") }, BUILDER(Create_func_json_merge_patch)}, { { STRING_WITH_LEN("JSON_MERGE_PRESERVE") }, BUILDER(Create_func_json_merge)}, + { { STRING_WITH_LEN("JSON_NORMALIZE") }, BUILDER(Create_func_json_normalize)}, { { STRING_WITH_LEN("JSON_QUERY") }, BUILDER(Create_func_json_query)}, { { STRING_WITH_LEN("JSON_QUOTE") }, BUILDER(Create_func_json_quote)}, { { STRING_WITH_LEN("JSON_OBJECT") }, BUILDER(Create_func_json_object)}, @@ -5601,6 +5646,7 @@ Native_func_registry func_array[] = { { STRING_WITH_LEN("MD5") }, BUILDER(Create_func_md5)}, { { STRING_WITH_LEN("MONTHNAME") }, BUILDER(Create_func_monthname)}, { { STRING_WITH_LEN("NAME_CONST") }, BUILDER(Create_func_name_const)}, + { {STRING_WITH_LEN("NATURAL_SORT_KEY")}, BUILDER(Create_func_natural_sort_key)}, { { STRING_WITH_LEN("NVL") }, BUILDER(Create_func_ifnull)}, { { STRING_WITH_LEN("NVL2") }, BUILDER(Create_func_nvl2)}, { { STRING_WITH_LEN("NULLIF") }, BUILDER(Create_func_nullif)}, @@ -5630,6 +5676,7 @@ Native_func_registry func_array[] = { { STRING_WITH_LEN("RTRIM") }, BUILDER(Create_func_rtrim)}, { { STRING_WITH_LEN("RTRIM_ORACLE") }, BUILDER(Create_func_rtrim_oracle)}, { { STRING_WITH_LEN("SEC_TO_TIME") }, BUILDER(Create_func_sec_to_time)}, + { { STRING_WITH_LEN("SFORMAT") }, BUILDER(Create_func_sformat)}, { { STRING_WITH_LEN("SHA") }, BUILDER(Create_func_sha)}, { { STRING_WITH_LEN("SHA1") }, BUILDER(Create_func_sha)}, { { STRING_WITH_LEN("SHA2") }, BUILDER(Create_func_sha2)}, @@ -5645,7 +5692,6 @@ Native_func_registry func_array[] = BUILDER(Create_func_substr_oracle)}, { { STRING_WITH_LEN("SUBSTRING_INDEX") }, BUILDER(Create_func_substr_index)}, { { STRING_WITH_LEN("SUBTIME") }, BUILDER(Create_func_subtime)}, - { { STRING_WITH_LEN("SYS_GUID") }, BUILDER(Create_func_sys_guid)}, { { STRING_WITH_LEN("TAN") }, BUILDER(Create_func_tan)}, { { STRING_WITH_LEN("TIMEDIFF") }, BUILDER(Create_func_timediff)}, { { STRING_WITH_LEN("TIME_FORMAT") }, BUILDER(Create_func_time_format)}, @@ -5661,7 +5707,6 @@ Native_func_registry func_array[] = { { STRING_WITH_LEN("UNIX_TIMESTAMP") }, BUILDER(Create_func_unix_timestamp)}, { { STRING_WITH_LEN("UPDATEXML") }, BUILDER(Create_func_xml_update)}, { { STRING_WITH_LEN("UPPER") }, BUILDER(Create_func_ucase)}, - { { STRING_WITH_LEN("UUID") }, BUILDER(Create_func_uuid)}, { { STRING_WITH_LEN("UUID_SHORT") }, BUILDER(Create_func_uuid_short)}, { { STRING_WITH_LEN("VERSION") }, BUILDER(Create_func_version)}, { { STRING_WITH_LEN("WEEKDAY") }, BUILDER(Create_func_weekday)}, diff --git a/sql/item_func.cc b/sql/item_func.cc index ed49733d15d..5b8a9f1fbfd 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -1004,7 +1004,8 @@ err: push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, ER_THD(thd, ER_WARN_DATA_OUT_OF_RANGE), - name.str, 1L); + name.str, + thd->get_stmt_da()->current_row_for_warning()); return dec; } diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 25f2c54cec0..fefb2a1c662 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -393,6 +393,66 @@ longlong Item_func_json_valid::val_int() } +bool Item_func_json_equals::fix_length_and_dec() +{ + if (Item_bool_func::fix_length_and_dec()) + return TRUE; + set_maybe_null(); + return FALSE; +} + + +longlong Item_func_json_equals::val_int() +{ + longlong result= 0; + + String a_tmp, b_tmp; + + String *a= args[0]->val_json(&a_tmp); + String *b= args[1]->val_json(&b_tmp); + + DYNAMIC_STRING a_res; + if (init_dynamic_string(&a_res, NULL, 0, 0)) + { + null_value= 1; + return 1; + } + + DYNAMIC_STRING b_res; + if (init_dynamic_string(&b_res, NULL, 0, 0)) + { + dynstr_free(&a_res); + null_value= 1; + return 1; + } + + if ((null_value= args[0]->null_value || args[1]->null_value)) + { + null_value= 1; + goto end; + } + + if (json_normalize(&a_res, a->ptr(), a->length(), a->charset())) + { + null_value= 1; + goto end; + } + + if (json_normalize(&b_res, b->ptr(), b->length(), b->charset())) + { + null_value= 1; + goto end; + } + + result= strcmp(a_res.str, b_res.str) ? 0 : 1; + +end: + dynstr_free(&b_res); + dynstr_free(&a_res); + return result; +} + + bool Item_func_json_exists::fix_length_and_dec() { if (Item_bool_func::fix_length_and_dec()) @@ -986,7 +1046,7 @@ my_decimal *Item_func_json_extract::val_decimal(my_decimal *to) case JSON_VALUE_ARRAY: case JSON_VALUE_FALSE: case JSON_VALUE_NULL: - case JSON_VALUE_UNINITALIZED: + case JSON_VALUE_UNINITIALIZED: break; }; } @@ -3886,3 +3946,48 @@ String* Item_func_json_objectagg::val_str(String* str) } +String *Item_func_json_normalize::val_str(String *buf) +{ + String tmp; + String *raw_json= args[0]->val_str(&tmp); + + DYNAMIC_STRING normalized_json; + if (init_dynamic_string(&normalized_json, NULL, 0, 0)) + { + null_value= 1; + return NULL; + } + + null_value= args[0]->null_value; + if (null_value) + goto end; + + if (json_normalize(&normalized_json, + raw_json->ptr(), raw_json->length(), + raw_json->charset())) + { + null_value= 1; + goto end; + } + + buf->length(0); + if (buf->append(normalized_json.str, normalized_json.length)) + { + null_value= 1; + goto end; + } + +end: + dynstr_free(&normalized_json); + return null_value ? NULL : buf; +} + + +bool Item_func_json_normalize::fix_length_and_dec() +{ + collation.set(&my_charset_utf8mb4_bin); + /* 0 becomes 0.0E0, thus one character becomes 5 chars */ + fix_char_length_ulonglong((ulonglong) args[0]->max_char_length() * 5); + set_maybe_null(); + return FALSE; +} diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h index 60600108320..e87b971269b 100644 --- a/sql/item_jsonfunc.h +++ b/sql/item_jsonfunc.h @@ -107,6 +107,23 @@ public: }; +class Item_func_json_equals: public Item_bool_func +{ +public: + Item_func_json_equals(THD *thd, Item *a, Item *b): + Item_bool_func(thd, a, b) {} + LEX_CSTRING func_name_cstring() const override + { + static LEX_CSTRING name= {STRING_WITH_LEN("json_equals") }; + return name; + } + bool fix_length_and_dec() override; + Item *get_copy(THD *thd) override + { return get_item_copy<Item_func_json_equals>(thd, this); } + longlong val_int() override; +}; + + class Item_func_json_exists: public Item_bool_func { protected: @@ -443,6 +460,24 @@ public: { return get_item_copy<Item_func_json_merge_patch>(thd, this); } }; + +class Item_func_json_normalize: public Item_json_func +{ +public: + Item_func_json_normalize(THD *thd, Item *a): + Item_json_func(thd, a) {} + String *val_str(String *) override; + LEX_CSTRING func_name_cstring() const override + { + static LEX_CSTRING name= {STRING_WITH_LEN("json_normalize") }; + return name; + } + bool fix_length_and_dec() override; + Item *get_copy(THD *thd) override + { return get_item_copy<Item_func_json_normalize>(thd, this); } +}; + + class Item_func_json_length: public Item_long_func { bool check_arguments() const override diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index eb9c59d31f7..d4bf28a9c21 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -55,6 +55,11 @@ C_MODE_END #include <sql_repl.h> #include "sql_statistics.h" +/* fmtlib include (https://fmt.dev/). */ +#define FMT_STATIC_THOUSANDS_SEPARATOR ',' +#define FMT_HEADER_ONLY 1 +#include "fmt/format-inl.h" + size_t username_char_length= USERNAME_CHAR_LENGTH; /* @@ -1303,6 +1308,138 @@ bool Item_func_replace::fix_length_and_dec() return FALSE; } +/* + this is done in the constructor to be in the same memroot as + the item itself +*/ +Item_func_sformat::Item_func_sformat(THD *thd, List<Item> &list) + : Item_str_func(thd, list) +{ + val_arg= new (thd->mem_root) String[arg_count]; +} + + +bool Item_func_sformat::fix_length_and_dec() +{ + if (!val_arg) + return TRUE; + + ulonglong char_length= 0; + + uint flags= MY_COLL_ALLOW_SUPERSET_CONV | + MY_COLL_ALLOW_COERCIBLE_CONV | + MY_COLL_ALLOW_NUMERIC_CONV; + + if (Type_std_attributes::agg_item_collations(collation, func_name_cstring(), + args, arg_count, flags, 1)) + return TRUE; + + DTCollation c= collation; + if (c.collation->mbminlen > 1) + c.collation= &my_charset_utf8mb4_bin; + + for (uint i=0 ; i < arg_count ; i++) + { + char_length+= args[i]->max_char_length(); + if (args[i]->result_type() == STRING_RESULT && + Type_std_attributes::agg_item_set_converter(c, func_name_cstring(), + args+i, 1, flags, 1)) + return TRUE; + } + + fix_char_length_ulonglong(char_length); + return FALSE; +} + +/* + allow fmt to take String arguments directly. + Inherit from string_view, so all string formatting works. + but {:p} doesn't, because it's not char*, not a pointer. +*/ +namespace fmt { + template <> struct formatter<String>: formatter<string_view> { + template <typename FormatContext> + auto format(String c, FormatContext& ctx) -> decltype(ctx.out()) { + string_view name = { c.ptr(), c.length() }; + return formatter<string_view>::format(name, ctx); + }; + }; +}; + +/* + SFORMAT(format_string, ...) + This function receives a formatting specification string and N parameters + (N >= 0), and it returns string formatted using the rules the user passed + in the specification. It uses fmtlib (https://fmt.dev/). +*/ +String *Item_func_sformat::val_str(String *res) +{ + DBUG_ASSERT(fixed()); + using ctx= fmt::format_context; + String *fmt_arg= NULL; + String *parg= NULL; + fmt::format_args::format_arg *vargs= NULL; + + null_value= true; + if (!(fmt_arg= args[0]->val_str(res))) + return NULL; + + if (!(vargs= new fmt::format_args::format_arg[arg_count - 1])) + return NULL; + + /* Creates the array of arguments for vformat */ + for (uint carg= 1; carg < arg_count; carg++) + { + switch (args[carg]->result_type()) + { + case INT_RESULT: + vargs[carg-1]= fmt::detail::make_arg<ctx>(args[carg]->val_int()); + break; + case DECIMAL_RESULT: // TODO + case REAL_RESULT: + if (args[carg]->field_type() == MYSQL_TYPE_FLOAT) + vargs[carg-1]= fmt::detail::make_arg<ctx>((float)args[carg]->val_real()); + else + vargs[carg-1]= fmt::detail::make_arg<ctx>(args[carg]->val_real()); + break; + case STRING_RESULT: + if (!(parg= args[carg]->val_str(&val_arg[carg-1]))) + { + delete [] vargs; + return NULL; + } + vargs[carg-1]= fmt::detail::make_arg<ctx>(*parg); + break; + case TIME_RESULT: // TODO + case ROW_RESULT: // TODO + default: + DBUG_ASSERT(0); + delete [] vargs; + return NULL; + } + } + + null_value= false; + /* Create the string output */ + try + { + auto text = fmt::vformat(fmt_arg->c_ptr_safe(), + fmt::format_args(vargs, arg_count-1)); + res->length(0); + res->set_charset(collation.collation); + res->append(text.c_str(), text.size(), fmt_arg->charset()); + } + catch (const fmt::format_error &ex) + { + THD *thd= current_thd; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + WARN_SFORMAT_ERROR, + ER_THD(thd, WARN_SFORMAT_ERROR), ex.what()); + null_value= true; + } + delete [] vargs; + return null_value ? NULL : res; +} /*********************************************************************/ bool Item_func_regexp_replace::fix_length_and_dec() @@ -4379,26 +4516,6 @@ err: #endif -String *Item_func_uuid::val_str(String *str) -{ - DBUG_ASSERT(fixed()); - uchar guid[MY_UUID_SIZE]; - size_t length= (without_separators ? - MY_UUID_ORACLE_STRING_LENGTH : - MY_UUID_STRING_LENGTH); - - str->alloc(length+1); - str->length(length); - str->set_charset(system_charset_info); - my_uuid(guid); - if (without_separators) - my_uuid2str_oracle(guid, (char *)str->ptr()); - else - my_uuid2str(guid, (char *)str->ptr()); - return str; -} - - Item_func_dyncol_create::Item_func_dyncol_create(THD *thd, List<Item> &args, DYNCALL_CREATE_DEF *dfs): Item_str_func(thd, args), defs(dfs), vals(0), keys_num(NULL), keys_str(NULL), @@ -5304,6 +5421,282 @@ String *Item_temptable_rowid::val_str(String *str) return &str_value; } +/** + Helper routine to encode length prefix + in natsort_encode_numeric_string(). + + The idea is so that bigger input numbers correspond + lexicographically bigger output strings. + + Note, that in real use the number would typically + small, as it only computes variable *length prefixes*. + + @param[in] n - the number + @param[in] s - output string + + @return - length of encoding + + Here is how encoding works + + - n is from 0 to 8 + Output string calculated as '0'+n (range '0' - '8') + + - n is from 9 to 17 + Output calculated as concat('9', '0' + n -9)' + Output range: '90'-'98' + + -n is from 18 to 26 + Output calculated as concat('99', '0' + n -18)' + Output range '990'-'998' + + - n is from 27 to SIZE_T_MAX + Output starts with '999', + then log10(n) is encoded as 2-digit decimal number + then the number itself is added. + Example : for 28 key is concat('999', '01' , '28') + i.e '9990128' + + Key length is 5 + ceil(log10(n)) + + Output range is + (64bit)'9990128' - '9991918446744073709551615' + (32bit)'9990128' - '999094294967295' +*/ + +/* Largest length of encoded string.*/ +static size_t natsort_encode_length_max(size_t n) +{ + return (n < 27) ? n/9+1 : 26; +} + +static void natsort_encode_length(size_t n, String* out) +{ + if (n < 27) + { + if (n >= 9) + out->fill(out->length() + n/9,'9'); + out->append(char(n % 9 + '0')); + return; + } + + size_t log10n= 0; + for (size_t tmp= n / 10; tmp; tmp/= 10) + log10n++; + out->fill(out->length() + 3, '9'); + out->append('0' + (char) (log10n / 10)); + out->append('0' + (char) (log10n % 10)); + out->append_ulonglong(n); +} + +enum class NATSORT_ERR +{ + SUCCESS= 0, + KEY_TOO_LARGE= 1, + ALLOC_ERROR= 2 +}; + +/* + Encode numeric string for natural sorting. + + @param[in] in - start of the numeric string + skipping leading zeros + + @param[in] n_digits - length of the string, + in characters, not counting leading zeros. + + @param[out] out - String to write to. The string should + have enough preallocated space to fit the encoded key. + + @return + NATSORT_ERR::SUCCESS - success + NATSORT_ERR::KEY_TOO_LARGE - out string does not have enough + space left to accomodate the key. + + + The resulting encoding of the numeric string is then + + CONCAT(natsort_encode_length(n_digits), in) +*/ +static NATSORT_ERR natsort_encode_numeric_string(const char *in, + size_t n_digits, + String *out) +{ + DBUG_ASSERT(in); + DBUG_ASSERT(n_digits); + + if (out->length() + natsort_encode_length_max(n_digits - 1) + n_digits > + out->alloced_length()) + return NATSORT_ERR::KEY_TOO_LARGE; + + natsort_encode_length(n_digits - 1, out); + out->append(in, n_digits); + return NATSORT_ERR::SUCCESS; +} + +/* + Calculate max size of the natsort key. + + A digit in string expands to 2 chars length_prefix , and the digit + + With even length L=2N, the largest key corresponds to input string + in form REPEAT(<digit><letter>,N) and the length of a key is + 2N + N = 3N + + With odd input length L=2N+1, largest key is built by appending + a digit at the end, with key length 3N+2 + +*/ +static size_t natsort_max_key_size(size_t input_size) +{ + return input_size + (input_size + 1)/2 ; +} + +/** + Convert a string to natural sort key. + @param[in] in - input string + @param[out] out - output string + @param[in] max_key_size - the maximum size of the output + key, in bytes. + @return NATSORT_ERR::SUCCESS - successful completion + NATSORT_ERR::ALLOC_ERROR - memory allocation error + NATSORT_ERR::KEY_TOO_LARGE - resulting key would exceed max_key_size +*/ +static NATSORT_ERR to_natsort_key(const String *in, String *out, + size_t max_key_size) +{ + size_t n_digits= 0; + size_t n_lead_zeros= 0; + size_t num_start; + size_t reserve_length= std::min( + natsort_max_key_size(in->length()) + MAX_BIGINT_WIDTH + 2, max_key_size); + + out->length(0); + out->set_charset(in->charset()); + + if (out->alloc((uint32) reserve_length)) + return NATSORT_ERR::ALLOC_ERROR; + + for (size_t pos= 0;; pos++) + { + char c= pos < in->length() ? (*in)[pos] : 0; + bool is_digit= (c >= '0' && c <= '9'); + if (!is_digit && (n_digits || n_lead_zeros)) + { + /* Handle end of digits run.*/ + if (!n_digits) + { + /*We only have zeros.*/ + n_lead_zeros--; + num_start= pos - 1; + n_digits= 1; + } + NATSORT_ERR err= natsort_encode_numeric_string( + in->ptr() + num_start, n_digits, out); + if (err != NATSORT_ERR::SUCCESS) + return err; + + /* Reset state.*/ + n_digits= 0; + num_start= size_t(-1); + n_lead_zeros= 0; + } + + if (pos == in->length()) + break; + + if (!is_digit) + { + if (out->length() == max_key_size) + return NATSORT_ERR::KEY_TOO_LARGE; + out->append(c); + } + else if (c == '0' && !n_digits) + n_lead_zeros++; + else if (!n_digits++) + num_start= pos; + } + return NATSORT_ERR::SUCCESS; +} + +String *Item_func_natural_sort_key::val_str(String *out) +{ + String *in= args[0]->val_str(); + if (args[0]->null_value || !in) + { + null_value= true; + return nullptr; + } + NATSORT_ERR err= NATSORT_ERR::SUCCESS; + CHARSET_INFO *cs= in->charset(); + ulong max_allowed_packet= current_thd->variables.max_allowed_packet; + uint errs; + String tmp; + /* + to_natsort_key() only support charsets where digits are represented by + a single byte in range 0x30-0x39. Almost everything is OK, just utf16/32 + won't do. Full ASCII compatibility is not required, so that SJIS and SWE7 + are fine. + */ + if (cs->mbminlen != 1) + { + if (tmp.copy(in, &my_charset_utf8mb4_bin, &errs)) + goto error_exit; + in= &tmp; + } + + err= to_natsort_key(in, out, max_allowed_packet / cs->mbminlen); + + if (err != NATSORT_ERR::SUCCESS) + { + if (err == NATSORT_ERR::KEY_TOO_LARGE) + { + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_ALLOWED_PACKET_OVERFLOWED, + ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(), + max_allowed_packet); + } + goto error_exit; + } + + if (cs->mbminlen != 1) + { + /* output string is now utf8, convert to input charset.*/ + if (tmp.copy(out, cs, &errs) || out->copy(tmp)) + goto error_exit; + } + null_value= false; + return out; + +error_exit: + null_value= true; + return nullptr; +} + +bool Item_func_natural_sort_key::fix_length_and_dec(void) +{ + if (agg_arg_charsets_for_string_result(collation, args, 1)) + return true; + DBUG_ASSERT(collation.collation != NULL); + uint32 max_char_len= + (uint32) natsort_max_key_size(args[0]->max_char_length()); + fix_char_length(max_char_len); + + set_maybe_null(args[0]->maybe_null() || + max_char_len * collation.collation->mbmaxlen > + current_thd->variables.max_allowed_packet); + return false; +} + +/** + Disable use in stored virtual functions. Temporarily(?), until + the encoding is stable. +*/ +bool Item_func_natural_sort_key::check_vcol_func_processor(void *arg) +{ + return mark_unsupported_function(func_name(), "()", arg, + VCOL_NON_DETERMINISTIC); +} + #ifdef WITH_WSREP #include "wsrep_mysqld.h" diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 3384cf5b369..ba61206d8d9 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -271,6 +271,25 @@ public: { return get_item_copy<Item_func_aes_decrypt>(thd, this); } }; +class Item_func_natural_sort_key : public Item_str_func +{ +public: + Item_func_natural_sort_key(THD *thd, Item *a) + : Item_str_func(thd, a){}; + String *val_str(String *) override; + LEX_CSTRING func_name_cstring() const override + { + static LEX_CSTRING name= {STRING_WITH_LEN("natural_sort_key")}; + return name; + } + bool fix_length_and_dec(void) override; + Item *get_copy(THD *thd) override + { + return get_item_copy<Item_func_natural_sort_key>(thd, this); + } + + bool check_vcol_func_processor(void *arg) override; +}; class Item_func_concat :public Item_str_func { @@ -586,6 +605,23 @@ public: { return get_item_copy<Item_func_substr>(thd, this); } }; +class Item_func_sformat :public Item_str_func +{ + String *val_arg; +public: + Item_func_sformat(THD *thd, List<Item> &list); + ~Item_func_sformat() { delete [] val_arg; } + String *val_str(String*) override; + bool fix_length_and_dec() override; + LEX_CSTRING func_name_cstring() const override + { + static LEX_CSTRING name= {STRING_WITH_LEN("sformat") }; + return name; + } + Item *get_copy(THD *thd) override + { return get_item_copy<Item_func_sformat>(thd, this); } +}; + class Item_func_substr_oracle :public Item_func_substr { protected: @@ -1996,40 +2032,6 @@ public: }; -class Item_func_uuid: public Item_str_func -{ - /* Set if uuid should be returned without separators (Oracle sys_guid) */ - bool without_separators; -public: -Item_func_uuid(THD *thd, bool without_separators_arg): Item_str_func(thd), - without_separators(without_separators_arg) - {} - bool fix_length_and_dec() override - { - collation.set(DTCollation_numeric()); - fix_char_length(without_separators ? MY_UUID_ORACLE_STRING_LENGTH : - MY_UUID_STRING_LENGTH); - return FALSE; - } - bool const_item() const override { return false; } - table_map used_tables() const override { return RAND_TABLE_BIT; } - LEX_CSTRING func_name_cstring() const override - { - static LEX_CSTRING mariadb_name= {STRING_WITH_LEN("uuid") }; - static LEX_CSTRING oracle_name= {STRING_WITH_LEN("sys_guid") }; - return without_separators ? oracle_name : mariadb_name; - } - String *val_str(String *) override; - bool check_vcol_func_processor(void *arg) override - { - return mark_unsupported_function(func_name(), "()", arg, - VCOL_NON_DETERMINISTIC); - } - Item *get_copy(THD *thd) override - { return get_item_copy<Item_func_uuid>(thd, this); } -}; - - class Item_func_dyncol_create: public Item_str_func { protected: diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index bafb81139e1..58680384f55 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1602,7 +1602,7 @@ bool Item_singlerow_subselect::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t Item_exists_subselect::Item_exists_subselect(THD *thd, st_select_lex *select_lex): - Item_subselect(thd), upper_not(NULL), abort_on_null(0), + Item_subselect(thd), upper_not(NULL), emb_on_expr_nest(NULL), optimizer(0), exists_transformed(0) { DBUG_ENTER("Item_exists_subselect::Item_exists_subselect"); @@ -1687,7 +1687,6 @@ Item_allany_subselect::Item_allany_subselect(THD *thd, Item * left_exp, func= func_creator(all_arg); init(select_lex, new (thd->mem_root) select_exists_subselect(thd, this)); max_columns= 1; - abort_on_null= 0; reset(); //if test_limit will fail then error will be reported to client test_limit(select_lex->master_unit()); @@ -2262,8 +2261,11 @@ bool Item_allany_subselect::is_maxmin_applicable(JOIN *join) Check if max/min optimization applicable: It is top item of WHERE condition. */ - return (abort_on_null || (upper_item && upper_item->is_top_level_item())) && - !(join->select_lex->master_unit()->uncacheable & ~UNCACHEABLE_EXPLAIN) && !func->eqne_op(); + return ((is_top_level_item() || + (upper_item && upper_item->is_top_level_item())) && + !(join->select_lex->master_unit()->uncacheable & + ~UNCACHEABLE_EXPLAIN) && + !func->eqne_op()); } @@ -2333,7 +2335,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join, ref_pointer_array[0], {STRING_WITH_LEN("<ref>")}, field_name)); - if (!abort_on_null && left_expr->maybe_null()) + if (!is_top_level_item() && left_expr->maybe_null()) { /* We can encounter "NULL IN (SELECT ...)". Wrap the added condition @@ -2366,7 +2368,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join, Item *orig_item= item; item= func->create(thd, expr, item); - if (!abort_on_null && orig_item->maybe_null()) + if (!is_top_level_item() && orig_item->maybe_null()) { having= new (thd->mem_root) Item_is_not_null_test(thd, this, having); if (left_expr->maybe_null()) @@ -2388,7 +2390,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join, If we may encounter NULL IN (SELECT ...) and care whether subquery result is NULL or FALSE, wrap condition in a trig_cond. */ - if (!abort_on_null && left_expr->maybe_null()) + if (!is_top_level_item() && left_expr->maybe_null()) { disable_cond_guard_for_const_null_left_expr(0); if (!(item= new (thd->mem_root) Item_func_trig_cond(thd, item, @@ -2418,7 +2420,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join, &select_lex->ref_pointer_array[0], no_matter_name, field_name)); - if (!abort_on_null && left_expr->maybe_null()) + if (!is_top_level_item() && left_expr->maybe_null()) { disable_cond_guard_for_const_null_left_expr(0); if (!(new_having= new (thd->mem_root) @@ -2617,7 +2619,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, list_ref)); Item *col_item= new (thd->mem_root) Item_cond_or(thd, item_eq, item_isnull); - if (!abort_on_null && left_expr->element_index(i)->maybe_null() && + if (!is_top_level_item() && left_expr->element_index(i)->maybe_null() && get_cond_guard(i)) { disable_cond_guard_for_const_null_left_expr(i); @@ -2636,7 +2638,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, ref_pointer_array[i], no_matter_name, list_ref)); - if (!abort_on_null && left_expr->element_index(i)->maybe_null() && + if (!is_top_level_item() && left_expr->element_index(i)->maybe_null() && get_cond_guard(i) ) { disable_cond_guard_for_const_null_left_expr(i); @@ -2677,7 +2679,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, ref_pointer_array[i], no_matter_name, list_ref)); - if (!abort_on_null && select_lex->ref_pointer_array[i]->maybe_null()) + if (!is_top_level_item() && select_lex->ref_pointer_array[i]->maybe_null()) { Item *having_col_item= new (thd->mem_root) @@ -2709,7 +2711,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, } *having_item= and_items(thd, *having_item, having_col_item); } - if (!abort_on_null && left_expr->element_index(i)->maybe_null() && + if (!is_top_level_item() && left_expr->element_index(i)->maybe_null() && get_cond_guard(i)) { if (!(item= new (thd->mem_root) @@ -3678,7 +3680,7 @@ bool Item_in_subselect::init_cond_guards() { DBUG_ASSERT(thd); uint cols_num= left_expr->cols(); - if (!abort_on_null && !pushed_cond_guards && + if (!is_top_level_item() && !pushed_cond_guards && (left_expr->maybe_null() || cols_num > 1)) { if (!(pushed_cond_guards= (bool*)thd->alloc(sizeof(bool) * cols_num))) @@ -4260,9 +4262,9 @@ bool subselect_uniquesubquery_engine::copy_ref_key(bool skip_constants) - NULL if select produces empty row set - FALSE otherwise. - In some cases (IN subselect is a top level item, i.e. abort_on_null==TRUE) - the caller doesn't distinguish between NULL and FALSE result and we just - return FALSE. + In some cases (IN subselect is a top level item, i.e. + is_top_level_item() == TRUE, the caller doesn't distinguish between NULL and + FALSE result and we just return FALSE. Otherwise we make a full table scan to see if there is at least one matching row. @@ -5117,7 +5119,7 @@ my_bitmap_init_memroot(MY_BITMAP *map, uint n_bits, MEM_ROOT *mem_root) if (!(bitmap_buf= (my_bitmap_map*) alloc_root(mem_root, bitmap_buffer_size(n_bits))) || - my_bitmap_init(map, bitmap_buf, n_bits, FALSE)) + my_bitmap_init(map, bitmap_buf, n_bits)) return TRUE; bitmap_clear_all(map); return FALSE; @@ -6010,7 +6012,7 @@ bool Ordered_key::alloc_keys_buffers() lookup offset. */ /* Notice that max_null_row is max array index, we need count, so +1. */ - if (my_bitmap_init(&null_key, NULL, (uint)(max_null_row + 1), FALSE)) + if (my_bitmap_init(&null_key, NULL, (uint)(max_null_row + 1))) return TRUE; cur_key_idx= HA_POS_ERROR; diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 426b76f8067..54e4f8f0ced 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -373,7 +373,6 @@ class Item_exists_subselect :public Item_subselect protected: Item_func_not *upper_not; bool value; /* value of this item (boolean: exists/not-exists) */ - bool abort_on_null; void init_length_and_dec(); bool select_prepare_to_be_in(); @@ -397,7 +396,7 @@ public: Item_exists_subselect(THD *thd_arg, st_select_lex *select_lex); Item_exists_subselect(THD *thd_arg): - Item_subselect(thd_arg), upper_not(NULL), abort_on_null(0), + Item_subselect(thd_arg), upper_not(NULL), emb_on_expr_nest(NULL), optimizer(0), exists_transformed(0) {} @@ -424,8 +423,6 @@ public: bool fix_length_and_dec() override; void print(String *str, enum_query_type query_type) override; bool select_transformer(JOIN *join) override; - void top_level_item() override { abort_on_null=1; } - bool is_top_level_item() const override { return abort_on_null; } bool exists2in_processor(void *opt_arg) override; Item* expr_cache_insert_transformer(THD *thd, uchar *unused) override; diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 2f4d34afc6d..a130be4f973 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -1232,13 +1232,13 @@ my_xpath_keyword(MY_XPATH *x, static Item *create_func_true(MY_XPATH *xpath, Item **args, uint nargs) { - return (Item*) &Item_true; + return (Item*) Item_true; } static Item *create_func_false(MY_XPATH *xpath, Item **args, uint nargs) { - return (Item*) &Item_false; + return (Item*) Item_false; } diff --git a/sql/json_table.cc b/sql/json_table.cc index aebc52b0832..e57dccd00c4 100644 --- a/sql/json_table.cc +++ b/sql/json_table.cc @@ -835,8 +835,7 @@ TABLE *create_table_for_function(THD *thd, TABLE_LIST *sql_table) my_bitmap_map* bitmaps= (my_bitmap_map*) thd->alloc(bitmap_buffer_size(field_count)); - my_bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count, - FALSE); + my_bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count); table->read_set= &table->def_read_set; bitmap_clear_all(table->read_set); table->alias_name_used= true; diff --git a/sql/lex.h b/sql/lex.h index cbf9d9d51b2..4ce88ccc2ee 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -110,6 +110,7 @@ SYMBOL symbols[] = { { "CHAIN", SYM(CHAIN_SYM)}, { "CHANGE", SYM(CHANGE)}, { "CHANGED", SYM(CHANGED)}, + { "CHANNEL", SYM(CHANNEL_SYM)}, { "CHAR", SYM(CHAR_SYM)}, { "CHARACTER", SYM(CHAR_SYM)}, { "CHARSET", SYM(CHARSET)}, @@ -567,6 +568,8 @@ SYMBOL symbols[] = { { "ROWTYPE", SYM(ROWTYPE_MARIADB_SYM)}, { "ROW_COUNT", SYM(ROW_COUNT_SYM)}, { "ROW_FORMAT", SYM(ROW_FORMAT_SYM)}, + /** sql_function and condition_property_name for GET DIAGNOSTICS */ + { "ROW_NUMBER", SYM(ROW_NUMBER_SYM)}, { "RTREE", SYM(RTREE_SYM)}, { "SAVEPOINT", SYM(SAVEPOINT_SYM)}, { "SCHEDULE", SYM(SCHEDULE_SYM)}, @@ -781,7 +784,6 @@ SYMBOL sql_functions[] = { { "PERCENTILE_CONT", SYM(PERCENTILE_CONT_SYM)}, { "PERCENTILE_DISC", SYM(PERCENTILE_DISC_SYM)}, { "RANK", SYM(RANK_SYM)}, - { "ROW_NUMBER", SYM(ROW_NUMBER_SYM)}, { "SESSION_USER", SYM(USER_SYM)}, { "STD", SYM(STD_SYM)}, { "STDDEV", SYM(STD_SYM)}, diff --git a/sql/lock.cc b/sql/lock.cc index 9f53ffb3a69..ef8e93cdfcc 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2020, MariaDB + Copyright (c) 2020, 2021, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1138,6 +1138,9 @@ void Global_read_lock::unlock_global_read_lock(THD *thd) else if (WSREP_NNULL(thd) && server_state.state() == Wsrep_server_state::s_synced) { + THD_STAGE_INFO(thd, stage_waiting_flow); + WSREP_DEBUG("unlock_global_read_lock: waiting for flow control for %s", + wsrep_thd_query(thd)); server_state.resume_and_resync(); wsrep_locked_seqno= WSREP_SEQNO_UNDEFINED; } diff --git a/sql/log.cc b/sql/log.cc index 1714c70f557..da7b2dbda71 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2622,12 +2622,11 @@ static void setup_windows_event_source() static int find_uniq_filename(char *name, ulong min_log_number_to_use, ulong *last_used_log_number) { - uint i; char buff[FN_REFLEN], ext_buf[FN_REFLEN]; struct st_my_dir *dir_info; struct fileinfo *file_info; ulong max_found= 0, next= 0, number= 0; - size_t buf_length, length; + size_t i, buf_length, length; char *start, *end; int error= 0; DBUG_ENTER("find_uniq_filename"); @@ -2958,7 +2957,7 @@ int MYSQL_BIN_LOG::generate_new_name(char *new_name, const char *log_name, fn_format(new_name, log_name, mysql_data_home, "", 4); if (!fn_ext(log_name)[0]) { - if (DBUG_EVALUATE_IF("binlog_inject_new_name_error", TRUE, FALSE) || + if (DBUG_IF("binlog_inject_new_name_error") || unlikely(find_uniq_filename(new_name, next_log_number, &last_used_log_number))) { @@ -3528,7 +3527,7 @@ bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg, mysql_file_seek(index_file_nr, 0L, MY_SEEK_END, MYF(0)), 0, MYF(MY_WME | MY_WAIT_IF_FULL), m_key_file_log_index_cache) || - DBUG_EVALUATE_IF("fault_injection_openning_index", 1, 0)) + DBUG_IF("fault_injection_openning_index")) { /* TODO: all operations creating/deleting the index file or a log, should @@ -3554,7 +3553,7 @@ bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg, open_purge_index_file(FALSE) || purge_index_entry(NULL, NULL, need_mutex) || close_purge_index_file() || - DBUG_EVALUATE_IF("fault_injection_recovering_index", 1, 0)) + DBUG_IF("fault_injection_recovering_index")) { sql_print_error("MYSQL_BIN_LOG::open_index_file failed to sync the index " "file."); @@ -3622,7 +3621,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name, if (open_purge_index_file(TRUE) || register_create_index_entry(log_file_name) || sync_purge_index_file() || - DBUG_EVALUATE_IF("fault_injection_registering_index", 1, 0)) + DBUG_IF("fault_injection_registering_index")) { /** TODO: @@ -3903,7 +3902,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name, As this is a new log file, we write the file name to the index file. As every time we write to the index file, we sync it. */ - if (DBUG_EVALUATE_IF("fault_injection_updating_index", 1, 0) || + if (DBUG_IF("fault_injection_updating_index") || my_b_write(&index_file, (uchar*) log_file_name, strlen(log_file_name)) || my_b_write(&index_file, (uchar*) "\n", 1) || @@ -5316,8 +5315,8 @@ int MYSQL_BIN_LOG::new_file_impl() r.checksum_alg= relay_log_checksum_alg; DBUG_ASSERT(!is_relay_log || relay_log_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF); - if (DBUG_EVALUATE_IF("fault_injection_new_file_rotate_event", - (error= close_on_error= TRUE), FALSE) || + if ((DBUG_IF("fault_injection_new_file_rotate_event") && + (error= close_on_error= TRUE)) || (error= write_event(&r))) { DBUG_EXECUTE_IF("fault_injection_new_file_rotate_event", errno= 2;); @@ -6710,7 +6709,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) Write the event. */ if (write_event(event_info, cache_data, file) || - DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0)) + DBUG_IF("injecting_fault_writing")) goto err; error= 0; @@ -8467,7 +8466,7 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader) DEBUG_SYNC(leader->thd, "commit_loop_entry_commit_ordered"); ++num_commits; if (current->cache_mngr->using_xa && likely(!current->error) && - DBUG_EVALUATE_IF("skip_commit_ordered", 0, 1)) + !DBUG_IF("skip_commit_ordered")) run_commit_ordered(current->thd, current->all); current->thd->wakeup_subsequent_commits(current->error); diff --git a/sql/log_event.cc b/sql/log_event.cc index b0d47ff496b..afca79b008a 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3321,8 +3321,7 @@ Rows_log_event::Rows_log_event(const uchar *buf, uint event_len, /* if my_bitmap_init fails, caught in is_valid() */ if (likely(!my_bitmap_init(&m_cols, m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL, - m_width, - false))) + m_width))) { DBUG_PRINT("debug", ("Reading from %p", ptr_after_width)); memcpy(m_cols.bitmap, ptr_after_width, (m_width + 7) / 8); @@ -3346,8 +3345,7 @@ Rows_log_event::Rows_log_event(const uchar *buf, uint event_len, /* if my_bitmap_init fails, caught in is_valid() */ if (likely(!my_bitmap_init(&m_cols_ai, m_width <= sizeof(m_bitbuf_ai)*8 ? m_bitbuf_ai : NULL, - m_width, - false))) + m_width))) { DBUG_PRINT("debug", ("Reading from %p", ptr_after_width)); memcpy(m_cols_ai.bitmap, ptr_after_width, (m_width + 7) / 8); diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc index 067bfb7c54b..7e0bf7d8e4c 100644 --- a/sql/log_event_client.cc +++ b/sql/log_event_client.cc @@ -1184,7 +1184,7 @@ void Rows_log_event::change_to_flashback_event(PRINT_EVENT_INFO *print_event_inf } /* Copying rows from the end to the begining into event */ - for (uint i= rows_arr.elements; i > 0; --i) + for (size_t i= rows_arr.elements; i > 0; --i) { LEX_STRING *one_row= dynamic_element(&rows_arr, i - 1, LEX_STRING*); diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 4e6b9e3f1c8..1990103598e 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1156,8 +1156,7 @@ Old_rows_log_event::Old_rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid, /* if my_bitmap_init fails, caught in is_valid() */ if (likely(!my_bitmap_init(&m_cols, m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL, - m_width, - false))) + m_width))) { /* Cols can be zero if this is a dummy binrows event */ if (likely(cols != NULL)) @@ -1232,8 +1231,7 @@ Old_rows_log_event::Old_rows_log_event(const uchar *buf, uint event_len, /* if my_bitmap_init fails, caught in is_valid() */ if (likely(!my_bitmap_init(&m_cols, m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL, - m_width, - false))) + m_width))) { DBUG_PRINT("debug", ("Reading from %p", ptr_after_width)); memcpy(m_cols.bitmap, ptr_after_width, (m_width + 7) / 8); diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc index ab9366bd651..cc103751b13 100644 --- a/sql/log_event_server.cc +++ b/sql/log_event_server.cc @@ -470,7 +470,7 @@ static void cleanup_load_tmpdir(LEX_CSTRING *connection_name) { MY_DIR *dirp; FILEINFO *file; - uint i; + size_t i; char dir[FN_REFLEN], fname[FN_REFLEN]; char prefbuf[31 + MAX_CONNECTION_NAME* MAX_FILENAME_MBWIDTH + 1]; DBUG_ENTER("cleanup_load_tmpdir"); @@ -491,7 +491,7 @@ static void cleanup_load_tmpdir(LEX_CSTRING *connection_name) load_data_tmp_prefix(prefbuf, connection_name); DBUG_PRINT("enter", ("dir: '%s' prefix: '%s'", dir, prefbuf)); - for (i=0 ; i < (uint)dirp->number_of_files; i++) + for (i=0 ; i < dirp->number_of_files; i++) { file=dirp->dir_entry+i; if (is_prefix(file->name, prefbuf)) @@ -5226,8 +5226,7 @@ Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid, /* if my_bitmap_init fails, caught in is_valid() */ if (likely(!my_bitmap_init(&m_cols, m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL, - m_width, - false))) + m_width))) { /* Cols can be zero if this is a dummy binrows event */ if (likely(cols != NULL)) @@ -6457,7 +6456,7 @@ int Table_map_log_event::do_apply_event(rpl_group_info *rgi) LEX_CSTRING tmp_tbl_name= {tname_mem, tname_mem_length }; table_list->init_one_table(&tmp_db_name, &tmp_tbl_name, 0, TL_WRITE); - table_list->table_id= DBUG_EVALUATE_IF("inject_tblmap_same_id_maps_diff_table", 0, m_table_id); + table_list->table_id= DBUG_IF("inject_tblmap_same_id_maps_diff_table") ? 0 : m_table_id; table_list->updating= 1; table_list->required_type= TABLE_TYPE_NORMAL; @@ -6697,7 +6696,7 @@ void Table_map_log_event::init_metadata_fields() if (binlog_row_metadata == BINLOG_ROW_METADATA_FULL) { - if (DBUG_EVALUATE_IF("dont_log_column_name", 0, init_column_name_field()) || + if ((!DBUG_IF("dont_log_column_name") && init_column_name_field()) || init_charset_field(&is_enum_or_set_field, ENUM_AND_SET_DEFAULT_CHARSET, ENUM_AND_SET_COLUMN_CHARSET) || init_set_str_value_field() || @@ -8279,8 +8278,7 @@ void Update_rows_log_event::init(MY_BITMAP const *cols) /* if my_bitmap_init fails, caught in is_valid() */ if (likely(!my_bitmap_init(&m_cols_ai, m_width <= sizeof(m_bitbuf_ai)*8 ? m_bitbuf_ai : NULL, - m_width, - false))) + m_width))) { /* Cols can be zero if this is a dummy binrows event */ if (likely(cols != NULL)) diff --git a/sql/mdl.cc b/sql/mdl.cc index bd5d6aa7db3..c2475bb3b91 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2007, 2012, Oracle and/or its affiliates. - Copyright (c) 2020, MariaDB + Copyright (c) 2020, 2021, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -2304,6 +2304,20 @@ MDL_context::acquire_lock(MDL_request *mdl_request, double lock_wait_timeout) DBUG_RETURN(TRUE); } +#ifdef WITH_WSREP + if (WSREP(get_thd())) + { + THD* requester= get_thd(); + bool requester_toi= wsrep_thd_is_toi(requester) || wsrep_thd_is_applying(requester); + WSREP_DEBUG("::acquire_lock is TOI %d for %s", requester_toi, + wsrep_thd_query(requester)); + if (requester_toi) + THD_STAGE_INFO(requester, stage_waiting_ddl); + else + THD_STAGE_INFO(requester, stage_waiting_isolation); + } +#endif /* WITH_WSREP */ + lock->m_waiting.add_ticket(ticket); /* diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 0bfb2f3640d..e7e5af8a2c8 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -369,6 +369,7 @@ uint volatile global_disable_checkpoint; ulong slow_start_timeout; #endif static MEM_ROOT startup_root; +MEM_ROOT read_only_root; /** @brief 'grant_option' is used to indicate if privileges needs @@ -641,7 +642,23 @@ struct system_variables max_system_variables; struct system_status_var global_status_var; MY_TMPDIR mysql_tmpdir_list; -MY_BITMAP temp_pool; +static MY_BITMAP temp_pool; +static mysql_mutex_t LOCK_temp_pool; + +void temp_pool_clear_bit(uint bit) +{ + mysql_mutex_lock(&LOCK_temp_pool); + bitmap_clear_bit(&temp_pool, bit); + mysql_mutex_unlock(&LOCK_temp_pool); +} + +uint temp_pool_set_next() +{ + mysql_mutex_lock(&LOCK_temp_pool); + uint res= bitmap_set_next(&temp_pool); + mysql_mutex_unlock(&LOCK_temp_pool); + return res; +} CHARSET_INFO *system_charset_info, *files_charset_info ; CHARSET_INFO *national_charset_info, *table_alias_charset; @@ -888,7 +905,7 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, key_LOCK_manager, key_LOCK_backup_log, key_LOCK_prepared_stmt_count, key_LOCK_rpl_status, key_LOCK_server_started, - key_LOCK_status, + key_LOCK_status, key_LOCK_temp_pool, key_LOCK_system_variables_hash, key_LOCK_thd_data, key_LOCK_thd_kill, key_LOCK_user_conn, key_LOCK_uuid_short_generator, key_LOG_LOCK_log, key_master_info_data_lock, key_master_info_run_lock, @@ -946,6 +963,7 @@ static PSI_mutex_info all_server_mutexes[]= { &key_hash_filo_lock, "hash_filo::lock", 0}, { &key_LOCK_active_mi, "LOCK_active_mi", PSI_FLAG_GLOBAL}, { &key_LOCK_backup_log, "LOCK_backup_log", PSI_FLAG_GLOBAL}, + { &key_LOCK_temp_pool, "LOCK_temp_pool", PSI_FLAG_GLOBAL}, { &key_LOCK_thread_id, "LOCK_thread_id", PSI_FLAG_GLOBAL}, { &key_LOCK_crypt, "LOCK_crypt", PSI_FLAG_GLOBAL}, { &key_LOCK_delayed_create, "LOCK_delayed_create", PSI_FLAG_GLOBAL}, @@ -1510,6 +1528,16 @@ static void end_ssl(); #ifndef EMBEDDED_LIBRARY +extern Atomic_counter<uint32_t> local_connection_thread_count; + +uint THD_count::connection_thd_count() +{ + return value() - + binlog_dump_thread_count - + local_connection_thread_count; +} + + /**************************************************************************** ** Code to end mysqld ****************************************************************************/ @@ -1533,7 +1561,7 @@ static my_bool kill_thread_phase_1(THD *thd, void *) if (thd->slave_thread || thd->is_binlog_dump_thread()) return 0; - if (DBUG_EVALUATE_IF("only_kill_system_threads", !thd->system_thread, 0)) + if (DBUG_IF("only_kill_system_threads") ? !thd->system_thread : 0) return 0; thd->awake(KILL_SERVER_HARD); return 0; @@ -1741,7 +1769,7 @@ static void close_connections(void) */ DBUG_PRINT("info", ("THD_count: %u", THD_count::value())); - for (int i= 0; (THD_count::value() - binlog_dump_thread_count) && i < 1000; i++) + for (int i= 0; (THD_count::connection_thd_count()) && i < 1000; i++) my_sleep(20000); if (global_system_variables.log_warnings) @@ -1756,12 +1784,12 @@ static void close_connections(void) /* All threads has now been aborted */ DBUG_PRINT("quit", ("Waiting for threads to die (count=%u)", THD_count::value())); - while (THD_count::value() - binlog_dump_thread_count) + while (THD_count::connection_thd_count()) my_sleep(1000); /* Kill phase 2 */ server_threads.iterate(kill_thread_phase_2); - for (uint64 i= 0; THD_count::value(); i++) + for (uint64 i= 0; THD_count::value() > local_connection_thread_count; i++) { /* This time the warnings are emitted within the loop to provide a @@ -1973,6 +2001,8 @@ static void clean_up(bool print_message) mysql_library_end(); finish_client_errs(); free_root(&startup_root, MYF(0)); + protect_root(&read_only_root, PROT_READ | PROT_WRITE); + free_root(&read_only_root, MYF(0)); cleanup_errmsgs(); free_error_messages(); /* Tell main we are ready */ @@ -2052,6 +2082,7 @@ static void clean_up_mutexes() mysql_mutex_destroy(&LOCK_active_mi); mysql_rwlock_destroy(&LOCK_ssl_refresh); mysql_mutex_destroy(&LOCK_backup_log); + mysql_mutex_destroy(&LOCK_temp_pool); mysql_rwlock_destroy(&LOCK_sys_init_connect); mysql_rwlock_destroy(&LOCK_sys_init_slave); mysql_mutex_destroy(&LOCK_global_system_variables); @@ -3240,7 +3271,7 @@ void my_message_sql(uint error, const char *str, myf MyFlags) { if (unlikely(MyFlags & ME_FATAL)) thd->is_fatal_error= 1; - (void) thd->raise_condition(error, NULL, level, str); + (void) thd->raise_condition(error, "\0\0\0\0\0", level, str); } else mysql_audit_general(0, MYSQL_AUDIT_GENERAL_ERROR, error, str); @@ -3354,7 +3385,6 @@ SHOW_VAR com_status_vars[]= { {"alter_server", STMT_STATUS(SQLCOM_ALTER_SERVER)}, {"alter_sequence", STMT_STATUS(SQLCOM_ALTER_SEQUENCE)}, {"alter_table", STMT_STATUS(SQLCOM_ALTER_TABLE)}, - {"alter_tablespace", STMT_STATUS(SQLCOM_ALTER_TABLESPACE)}, {"alter_user", STMT_STATUS(SQLCOM_ALTER_USER)}, {"analyze", STMT_STATUS(SQLCOM_ANALYZE)}, {"assign_to_keycache", STMT_STATUS(SQLCOM_ASSIGN_TO_KEYCACHE)}, @@ -3723,6 +3753,8 @@ static int init_early_variables() set_malloc_size_cb(my_malloc_size_cb_func); global_status_var.global_memory_used= 0; init_alloc_root(PSI_NOT_INSTRUMENTED, &startup_root, 1024, 0, MYF(0)); + init_alloc_root(PSI_NOT_INSTRUMENTED, &read_only_root, 1024, 0, + MYF(MY_ROOT_USE_MPROTECT)); return 0; } @@ -4227,7 +4259,7 @@ static int init_common_variables() #endif /* defined(ENABLED_DEBUG_SYNC) */ #if (ENABLE_TEMP_POOL) - if (use_temp_pool && my_bitmap_init(&temp_pool,0,1024,1)) + if (use_temp_pool && my_bitmap_init(&temp_pool,0,1024)) return 1; #else use_temp_pool= 0; @@ -4354,6 +4386,7 @@ static int init_thread_environment() mysql_mutex_init(key_LOCK_commit_ordered, &LOCK_commit_ordered, MY_MUTEX_INIT_SLOW); mysql_mutex_init(key_LOCK_backup_log, &LOCK_backup_log, MY_MUTEX_INIT_FAST); + mysql_mutex_init(key_LOCK_temp_pool, &LOCK_temp_pool, MY_MUTEX_INIT_FAST); #ifdef HAVE_OPENSSL mysql_mutex_init(key_LOCK_des_key_file, @@ -5035,6 +5068,7 @@ static int init_server_components() init_global_table_stats(); init_global_index_stats(); + init_update_queries(); /* Allow storage engine to give real error messages */ if (unlikely(ha_init_errors())) @@ -5042,6 +5076,9 @@ static int init_server_components() tc_log= 0; // ha_initialize_handlerton() needs that + if (!opt_abort && ddl_log_initialize()) + unireg_abort(1); + if (plugin_init(&remaining_argc, remaining_argv, (opt_noacl ? PLUGIN_INIT_SKIP_PLUGIN_TABLE : 0) | (opt_abort ? PLUGIN_INIT_SKIP_INITIALIZATION : 0))) @@ -5286,9 +5323,6 @@ static int init_server_components() } #endif - if (ddl_log_initialize()) - unireg_abort(1); - tc_log= get_tc_log_implementation(); if (tc_log->open(opt_bin_log ? opt_bin_logname : opt_tc_log_file)) @@ -5369,12 +5403,15 @@ static int init_server_components() ft_init_stopwords(); init_max_user_conn(); - init_update_queries(); init_global_user_stats(); init_global_client_stats(); if (!opt_bootstrap) servers_init(0); init_status_vars(); + Item_false= new (&read_only_root) Item_bool_static("FALSE", 0); + Item_true= new (&read_only_root) Item_bool_static("TRUE", 1); + DBUG_ASSERT(Item_false); + DBUG_RETURN(0); } @@ -5738,6 +5775,9 @@ int mysqld_main(int argc, char **argv) } #endif /* WITH_WSREP */ + /* Protect read_only_root against writes */ + protect_root(&read_only_root, PROT_READ); + if (opt_bootstrap) { select_thread_in_use= 0; // Allow 'kill' to work @@ -9161,6 +9201,14 @@ PSI_stage_info stage_starting= { 0, "starting", 0}; PSI_stage_info stage_waiting_for_flush= { 0, "Waiting for non trans tables to be flushed", 0}; PSI_stage_info stage_waiting_for_ddl= { 0, "Waiting for DDLs", 0}; +#ifdef WITH_WSREP +// Aditional Galera thread states +PSI_stage_info stage_waiting_isolation= { 0, "Waiting to execute in isolation", 0}; +PSI_stage_info stage_waiting_certification= {0, "Waiting for certification", 0}; +PSI_stage_info stage_waiting_ddl= {0, "Waiting for TOI DDL", 0}; +PSI_stage_info stage_waiting_flow= {0, "Waiting for flow control", 0}; +#endif /* WITH_WSREP */ + PSI_memory_key key_memory_DATE_TIME_FORMAT; PSI_memory_key key_memory_DDL_LOG_MEMORY_ENTRY; PSI_memory_key key_memory_Event_queue_element_for_exec_names; @@ -9380,6 +9428,13 @@ PSI_stage_info *all_server_stages[]= & stage_reading_semi_sync_ack, & stage_waiting_for_deadlock_kill, & stage_starting +#ifdef WITH_WSREP + , + & stage_waiting_isolation, + & stage_waiting_certification, + & stage_waiting_ddl, + & stage_waiting_flow +#endif /* WITH_WSREP */ }; PSI_socket_key key_socket_tcpip, key_socket_unix, key_socket_client_connection; diff --git a/sql/mysqld.h b/sql/mysqld.h index d0a33fabb51..8c0b92c6446 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -1,5 +1,5 @@ /* Copyright (c) 2006, 2016, Oracle and/or its affiliates. - Copyright (c) 2010, 2020, MariaDB Corporation. + Copyright (c) 2010, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -100,7 +100,9 @@ extern CHARSET_INFO *error_message_charset_info; extern CHARSET_INFO *character_set_filesystem; -extern MY_BITMAP temp_pool; +void temp_pool_clear_bit(uint bit); +uint temp_pool_set_next(); + extern bool opt_large_files; extern bool opt_update_log, opt_bin_log, opt_error_log, opt_bin_log_compress; extern uint opt_bin_log_compress_min_len; @@ -677,6 +679,13 @@ extern PSI_stage_info stage_slave_background_process_request; extern PSI_stage_info stage_slave_background_wait_request; extern PSI_stage_info stage_waiting_for_deadlock_kill; extern PSI_stage_info stage_starting; +#ifdef WITH_WSREP +// Aditional Galera thread states +extern PSI_stage_info stage_waiting_isolation; +extern PSI_stage_info stage_waiting_certification; +extern PSI_stage_info stage_waiting_ddl; +extern PSI_stage_info stage_waiting_flow; +#endif /* WITH_WSREP */ #ifdef HAVE_PSI_STATEMENT_INTERFACE /** diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 80944e97eb7..06063cb9ae1 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1308,7 +1308,7 @@ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr, *create_error= 1; } else - my_bitmap_init(&column_bitmap, bitmap, head->s->fields, FALSE); + my_bitmap_init(&column_bitmap, bitmap, head->s->fields); DBUG_VOID_RETURN; } @@ -2577,7 +2577,7 @@ static int fill_used_fields_bitmap(PARAM *param) param->fields_bitmap_size= table->s->column_bitmap_size; if (!(tmp= (my_bitmap_map*) alloc_root(param->mem_root, param->fields_bitmap_size)) || - my_bitmap_init(¶m->needed_fields, tmp, table->s->fields, FALSE)) + my_bitmap_init(¶m->needed_fields, tmp, table->s->fields)) return 1; bitmap_copy(¶m->needed_fields, table->read_set); @@ -3347,7 +3347,7 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond) my_bitmap_map* buf; if (!(buf= (my_bitmap_map*)thd->alloc(table->s->column_bitmap_size))) DBUG_RETURN(TRUE); - my_bitmap_init(&handled_columns, buf, table->s->fields, FALSE); + my_bitmap_init(&handled_columns, buf, table->s->fields); /* Calculate the selectivity of the range conditions supported by indexes. @@ -4137,7 +4137,7 @@ static int find_used_partitions_imerge_list(PART_PRUNE_PARAM *ppar, */ return find_used_partitions_imerge(ppar, merges.head()); } - my_bitmap_init(&all_merges, bitmap_buf, n_bits, FALSE); + my_bitmap_init(&all_merges, bitmap_buf, n_bits); bitmap_set_prefix(&all_merges, n_bits); List_iterator<SEL_IMERGE> it(merges); @@ -4793,8 +4793,7 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar) uint32 bufsize= bitmap_buffer_size(ppar->part_info->num_subparts); if (!(buf= (my_bitmap_map*) alloc_root(alloc, bufsize))) return TRUE; - my_bitmap_init(&ppar->subparts_bitmap, buf, ppar->part_info->num_subparts, - FALSE); + my_bitmap_init(&ppar->subparts_bitmap, buf, ppar->part_info->num_subparts); } range_par->key_parts= key_part; Field **field= (ppar->part_fields)? part_info->part_field_array : @@ -5621,7 +5620,7 @@ bool create_fields_bitmap(PARAM *param, MY_BITMAP *fields_bitmap) if (!(bitmap_buf= (my_bitmap_map *) alloc_root(param->mem_root, param->fields_bitmap_size))) return TRUE; - if (my_bitmap_init(fields_bitmap, bitmap_buf, param->table->s->fields, FALSE)) + if (my_bitmap_init(fields_bitmap, bitmap_buf, param->table->s->fields)) return TRUE; return FALSE; @@ -6533,7 +6532,7 @@ ROR_SCAN_INFO *make_ror_scan(const PARAM *param, int idx, SEL_ARG *sel_arg) DBUG_RETURN(NULL); if (my_bitmap_init(&ror_scan->covered_fields, bitmap_buf, - param->table->s->fields, FALSE)) + param->table->s->fields)) DBUG_RETURN(NULL); bitmap_clear_all(&ror_scan->covered_fields); @@ -6650,8 +6649,7 @@ ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param) if (!(buf= (my_bitmap_map*) alloc_root(param->mem_root, param->fields_bitmap_size))) return NULL; - if (my_bitmap_init(&info->covered_fields, buf, param->table->s->fields, - FALSE)) + if (my_bitmap_init(&info->covered_fields, buf, param->table->s->fields)) return NULL; info->is_covering= FALSE; info->index_scan_costs= 0.0; @@ -7296,7 +7294,7 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, param->fields_bitmap_size); if (!covered_fields->bitmap || my_bitmap_init(covered_fields, covered_fields->bitmap, - param->table->s->fields, FALSE)) + param->table->s->fields)) DBUG_RETURN(0); bitmap_clear_all(covered_fields); @@ -12611,10 +12609,10 @@ int QUICK_RANGE_SELECT::reset() if (!mrr_buf_desc) empty_buf.buffer= empty_buf.buffer_end= empty_buf.end_of_used_area= NULL; - - error= file->multi_range_read_init(&seq_funcs, (void*)this, ranges.elements, - mrr_flags, mrr_buf_desc? mrr_buf_desc: - &empty_buf); + + error= file->multi_range_read_init(&seq_funcs, (void*)this, + (uint)ranges.elements, mrr_flags, + mrr_buf_desc? mrr_buf_desc: &empty_buf); err: /* Restore bitmaps set on entry */ if (in_ror_merged_scan) @@ -12726,7 +12724,7 @@ int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length, } } - uint count= ranges.elements - (uint)(cur_range - (QUICK_RANGE**) ranges.buffer); + size_t count= ranges.elements - (size_t)(cur_range - (QUICK_RANGE**) ranges.buffer); if (count == 0) { /* Ranges have already been used up before. None is left for read. */ @@ -12771,7 +12769,7 @@ int QUICK_RANGE_SELECT_GEOM::get_next() DBUG_RETURN(result); } - uint count= ranges.elements - (uint)(cur_range - (QUICK_RANGE**) ranges.buffer); + size_t count= ranges.elements - (size_t)(cur_range - (QUICK_RANGE**) ranges.buffer); if (count == 0) { /* Ranges have already been used up before. None is left for read. */ @@ -12812,9 +12810,9 @@ int QUICK_RANGE_SELECT_GEOM::get_next() bool QUICK_RANGE_SELECT::row_in_ranges() { QUICK_RANGE *res; - uint min= 0; - uint max= ranges.elements - 1; - uint mid= (max + min)/2; + size_t min= 0; + size_t max= ranges.elements - 1; + size_t mid= (max + min)/2; while (min != max) { @@ -15800,7 +15798,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_max_in_range() DBUG_ASSERT(min_max_ranges.elements > 0); - for (uint range_idx= min_max_ranges.elements; range_idx > 0; range_idx--) + for (size_t range_idx= min_max_ranges.elements; range_idx > 0; range_idx--) { /* Search from the right-most range to the left. */ get_dynamic(&min_max_ranges, (uchar*)&cur_range, range_idx - 1); @@ -16346,7 +16344,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::dbug_dump(int indent, bool verbose) } if (min_max_ranges.elements > 0) { - fprintf(DBUG_FILE, "%*susing %d quick_ranges for MIN/MAX:\n", + fprintf(DBUG_FILE, "%*susing %zu quick_ranges for MIN/MAX:\n", indent, "", min_max_ranges.elements); } } diff --git a/sql/opt_split.cc b/sql/opt_split.cc index 41b8acf5dcb..ad90bf32315 100644 --- a/sql/opt_split.cc +++ b/sql/opt_split.cc @@ -704,7 +704,7 @@ double spl_postjoin_oper_cost(THD *thd, double join_record_count, uint rec_len) void JOIN::add_keyuses_for_splitting() { uint i; - uint idx; + size_t idx; KEYUSE_EXT *keyuse_ext; KEYUSE_EXT keyuse_ext_end; double oper_cost; diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 99f75d9ad2f..596b5169659 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -4485,7 +4485,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd) STEP 1: Get temporary table name */ if (use_temp_pool && !(test_flags & TEST_KEEP_TMP_TABLES)) - temp_pool_slot = bitmap_lock_set_next(&temp_pool); + temp_pool_slot = temp_pool_set_next(); if (temp_pool_slot != MY_BIT_NONE) // we got a slot sprintf(path, "%s-subquery-%lx-%i", tmp_file_prefix, @@ -4522,7 +4522,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd) NullS)) { if (temp_pool_slot != MY_BIT_NONE) - bitmap_lock_clear_bit(&temp_pool, temp_pool_slot); + temp_pool_clear_bit(temp_pool_slot); DBUG_RETURN(TRUE); } strmov(tmpname,path); @@ -4732,7 +4732,7 @@ err: thd->mem_root= mem_root_save; free_tmp_table(thd,table); /* purecov: inspected */ if (temp_pool_slot != MY_BIT_NONE) - bitmap_lock_clear_bit(&temp_pool, temp_pool_slot); + temp_pool_clear_bit(temp_pool_slot); DBUG_RETURN(TRUE); /* purecov: inspected */ } diff --git a/sql/opt_table_elimination.cc b/sql/opt_table_elimination.cc index a6f0ac24719..8c4720bdec4 100644 --- a/sql/opt_table_elimination.cc +++ b/sql/opt_table_elimination.cc @@ -1070,7 +1070,7 @@ bool Dep_analysis_context::setup_equality_modules_deps(List<Dep_module> void *buf; if (!(buf= thd->alloc(bitmap_buffer_size(offset))) || - my_bitmap_init(&expr_deps, (my_bitmap_map*)buf, offset, FALSE)) + my_bitmap_init(&expr_deps, (my_bitmap_map*)buf, offset)) { DBUG_RETURN(TRUE); /* purecov: inspected */ } diff --git a/sql/partition_element.h b/sql/partition_element.h index e0a519065cc..c551baa3092 100644 --- a/sql/partition_element.h +++ b/sql/partition_element.h @@ -111,7 +111,6 @@ public: ha_rows part_min_rows; longlong range_value; const char *partition_name; - const char *tablespace_name; struct st_ddl_log_memory_entry *log_entry; const char* part_comment; const char* data_file_name; @@ -129,7 +128,7 @@ public: partition_element() : part_max_rows(0), part_min_rows(0), range_value(0), - partition_name(NULL), tablespace_name(NULL), + partition_name(NULL), log_entry(NULL), part_comment(NULL), data_file_name(NULL), index_file_name(NULL), engine_type(NULL), connect_string(null_clex_str), part_state(PART_NORMAL), @@ -143,7 +142,6 @@ public: : part_max_rows(part_elem->part_max_rows), part_min_rows(part_elem->part_min_rows), range_value(0), partition_name(NULL), - tablespace_name(part_elem->tablespace_name), log_entry(NULL), part_comment(part_elem->part_comment), data_file_name(part_elem->data_file_name), diff --git a/sql/partition_info.cc b/sql/partition_info.cc index fd92e437cac..62a3092f369 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -2426,7 +2426,7 @@ bool partition_info::has_same_partitioning(partition_info *new_part_info) partition_element *new_part_elem= new_part_it++; /* The following must match: - partition_name, tablespace_name, data_file_name, index_file_name, + partition_name, data_file_name, index_file_name, engine_type, part_max_rows, part_min_rows, nodegroup_id. (max_value, signed_flag, has_null_value only on partition level, RANGE/LIST) @@ -2512,9 +2512,7 @@ bool partition_info::has_same_partitioning(partition_info *new_part_info) if (strcmp_null(sub_part_elem->data_file_name, new_sub_part_elem->data_file_name) || strcmp_null(sub_part_elem->index_file_name, - new_sub_part_elem->index_file_name) || - strcmp_null(sub_part_elem->tablespace_name, - new_sub_part_elem->tablespace_name)) + new_sub_part_elem->index_file_name)) DBUG_RETURN(false); } while (++j < num_subparts); @@ -2530,9 +2528,7 @@ bool partition_info::has_same_partitioning(partition_info *new_part_info) if (strcmp_null(part_elem->data_file_name, new_part_elem->data_file_name) || strcmp_null(part_elem->index_file_name, - new_part_elem->index_file_name) || - strcmp_null(part_elem->tablespace_name, - new_part_elem->tablespace_name)) + new_part_elem->index_file_name)) DBUG_RETURN(false); } } while (++i < num_parts); diff --git a/sql/partition_info.h b/sql/partition_info.h index 0656238ec07..995147d6766 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -79,7 +79,7 @@ struct Vers_part_info : public Sql_alloc partition_element *hist_part; }; -class partition_info : public Sql_alloc +class partition_info : public DDL_LOG_STATE, public Sql_alloc { public: /* @@ -154,10 +154,6 @@ public: Item *item_free_list; - struct st_ddl_log_memory_entry *first_log_entry; - struct st_ddl_log_memory_entry *exec_log_entry; - struct st_ddl_log_memory_entry *frm_log_entry; - /* Bitmaps of partitions used by the current query. * read_partitions - partitions to be used for reading. @@ -297,7 +293,6 @@ public: part_field_buffers(NULL), subpart_field_buffers(NULL), restore_part_field_ptrs(NULL), restore_subpart_field_ptrs(NULL), part_expr(NULL), subpart_expr(NULL), item_free_list(NULL), - first_log_entry(NULL), exec_log_entry(NULL), frm_log_entry(NULL), bitmaps_are_initialized(FALSE), list_array(NULL), vers_info(NULL), err_value(0), part_info_string(NULL), @@ -319,6 +314,7 @@ public: is_auto_partitioned(FALSE), has_null_value(FALSE), column_list(FALSE) { + bzero((DDL_LOG_STATE *) this, sizeof(DDL_LOG_STATE)); all_fields_in_PF.clear_all(); all_fields_in_PPF.clear_all(); all_fields_in_SPF.clear_all(); diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc index eecc6040051..306ae878060 100644 --- a/sql/rpl_gtid.cc +++ b/sql/rpl_gtid.cc @@ -249,8 +249,9 @@ rpl_slave_state::rpl_slave_state() { mysql_mutex_init(key_LOCK_slave_state, &LOCK_slave_state, MY_MUTEX_INIT_SLOW); - my_hash_init(PSI_INSTRUMENT_ME, &hash, &my_charset_bin, 32, offsetof(element, domain_id), - sizeof(uint32), NULL, rpl_slave_state_free_element, HASH_UNIQUE); + my_hash_init(PSI_INSTRUMENT_ME, &hash, &my_charset_bin, 32, + offsetof(element, domain_id), sizeof(element::domain_id), + NULL, rpl_slave_state_free_element, HASH_UNIQUE); my_init_dynamic_array(PSI_INSTRUMENT_ME, >id_sort_array, sizeof(rpl_gtid), 8, 8, MYF(0)); } @@ -366,7 +367,8 @@ rpl_slave_state::get_element(uint32 domain_id) { struct element *elem; - elem= (element *)my_hash_search(&hash, (const uchar *)&domain_id, 0); + elem= (element *)my_hash_search(&hash, (const uchar *)&domain_id, + sizeof(domain_id)); if (elem) return elem; @@ -402,7 +404,8 @@ rpl_slave_state::put_back_list(list_element *list) list_element *next= list->next; if ((!e || e->domain_id != list->domain_id) && - !(e= (element *)my_hash_search(&hash, (const uchar *)&list->domain_id, 0))) + !(e= (element *)my_hash_search(&hash, (const uchar *)&list->domain_id, + sizeof(list->domain_id)))) { err= 1; goto end; @@ -1107,8 +1110,8 @@ rpl_slave_state::iterate(int (*cb)(rpl_gtid *, void *), void *data, bool locked= false; my_hash_init(PSI_INSTRUMENT_ME, >id_hash, &my_charset_bin, 32, - offsetof(rpl_gtid, domain_id), sizeof(uint32), NULL, NULL, - HASH_UNIQUE); + offsetof(rpl_gtid, domain_id), sizeof(rpl_gtid::domain_id), + NULL, NULL, HASH_UNIQUE); for (i= 0; i < num_extra; ++i) if (extra_gtids[i].server_id == global_system_variables.server_id && my_hash_insert(>id_hash, (uchar *)(&extra_gtids[i]))) @@ -1143,7 +1146,8 @@ rpl_slave_state::iterate(int (*cb)(rpl_gtid *, void *), void *data, } /* Check if we have something newer in the extra list. */ - rec= my_hash_search(>id_hash, (const uchar *)&best_gtid.domain_id, 0); + rec= my_hash_search(>id_hash, (const uchar *)&best_gtid.domain_id, + sizeof(best_gtid.domain_id)); if (rec) { gtid= (rpl_gtid *)rec; @@ -1243,7 +1247,8 @@ rpl_slave_state::domain_to_gtid(uint32 domain_id, rpl_gtid *out_gtid) uint64 best_sub_id; mysql_mutex_lock(&LOCK_slave_state); - elem= (element *)my_hash_search(&hash, (const uchar *)&domain_id, 0); + elem= (element *)my_hash_search(&hash, (const uchar *)&domain_id, + sizeof(domain_id)); if (!elem || !(list= elem->list)) { mysql_mutex_unlock(&LOCK_slave_state); @@ -1477,8 +1482,9 @@ rpl_slave_state::alloc_gtid_pos_table(LEX_CSTRING *table_name, void *hton, void rpl_binlog_state::init() { - my_hash_init(PSI_INSTRUMENT_ME, &hash, &my_charset_bin, 32, offsetof(element, domain_id), - sizeof(uint32), NULL, my_free, HASH_UNIQUE); + my_hash_init(PSI_INSTRUMENT_ME, &hash, &my_charset_bin, 32, + offsetof(element, domain_id), sizeof(element::domain_id), + NULL, my_free, HASH_UNIQUE); my_init_dynamic_array(PSI_INSTRUMENT_ME, >id_sort_array, sizeof(rpl_gtid), 8, 8, MYF(0)); mysql_mutex_init(key_LOCK_binlog_state, &LOCK_binlog_state, MY_MUTEX_INIT_SLOW); @@ -1580,7 +1586,8 @@ rpl_binlog_state::update_nolock(const struct rpl_gtid *gtid, bool strict) element *elem; if ((elem= (element *)my_hash_search(&hash, - (const uchar *)(>id->domain_id), 0))) + (const uchar *)(>id->domain_id), + sizeof(gtid->domain_id)))) { if (strict && elem->last_gtid && elem->last_gtid->seq_no >= gtid->seq_no) { @@ -1628,7 +1635,8 @@ rpl_binlog_state::update_with_next_gtid(uint32 domain_id, uint32 server_id, gtid->server_id= server_id; mysql_mutex_lock(&LOCK_binlog_state); - if ((elem= (element *)my_hash_search(&hash, (const uchar *)(&domain_id), 0))) + if ((elem= (element *)my_hash_search(&hash, (const uchar *)(&domain_id), + sizeof(domain_id)))) { gtid->seq_no= ++elem->seq_no_counter; if (!elem->update_element(gtid)) @@ -1667,7 +1675,8 @@ rpl_binlog_state::element::update_element(const rpl_gtid *gtid) } lookup_gtid= (rpl_gtid *) - my_hash_search(&hash, (const uchar *)>id->server_id, 0); + my_hash_search(&hash, (const uchar *)>id->server_id, + sizeof(gtid->server_id)); if (lookup_gtid) { lookup_gtid->seq_no= gtid->seq_no; @@ -1705,8 +1714,8 @@ rpl_binlog_state::alloc_element_nolock(const rpl_gtid *gtid) { elem->domain_id= gtid->domain_id; my_hash_init(PSI_INSTRUMENT_ME, &elem->hash, &my_charset_bin, 32, - offsetof(rpl_gtid, server_id), sizeof(uint32), NULL, my_free, - HASH_UNIQUE); + offsetof(rpl_gtid, server_id), sizeof(rpl_gtid::domain_id), + NULL, my_free, HASH_UNIQUE); elem->last_gtid= lookup_gtid; elem->seq_no_counter= gtid->seq_no; memcpy(lookup_gtid, gtid, sizeof(*lookup_gtid)); @@ -1741,7 +1750,8 @@ rpl_binlog_state::check_strict_sequence(uint32 domain_id, uint32 server_id, mysql_mutex_lock(&LOCK_binlog_state); if ((elem= (element *)my_hash_search(&hash, - (const uchar *)(&domain_id), 0)) && + (const uchar *)(&domain_id), + sizeof(domain_id))) && elem->last_gtid && elem->last_gtid->seq_no >= seq_no) { my_error(ER_GTID_STRICT_OUT_OF_ORDER, MYF(0), domain_id, server_id, seq_no, @@ -1768,7 +1778,8 @@ rpl_binlog_state::bump_seq_no_if_needed(uint32 domain_id, uint64 seq_no) int res; mysql_mutex_lock(&LOCK_binlog_state); - if ((elem= (element *)my_hash_search(&hash, (const uchar *)(&domain_id), 0))) + if ((elem= (element *)my_hash_search(&hash, (const uchar *)(&domain_id), + sizeof(domain_id)))) { if (elem->seq_no_counter < seq_no) elem->seq_no_counter= seq_no; @@ -1786,8 +1797,8 @@ rpl_binlog_state::bump_seq_no_if_needed(uint32 domain_id, uint64 seq_no) elem->domain_id= domain_id; my_hash_init(PSI_INSTRUMENT_ME, &elem->hash, &my_charset_bin, 32, - offsetof(rpl_gtid, server_id), sizeof(uint32), NULL, my_free, - HASH_UNIQUE); + offsetof(rpl_gtid, server_id), sizeof(rpl_gtid::server_id), + NULL, my_free, HASH_UNIQUE); elem->last_gtid= NULL; elem->seq_no_counter= seq_no; if (0 == my_hash_insert(&hash, (const uchar *)elem)) @@ -1891,9 +1902,11 @@ rpl_gtid * rpl_binlog_state::find_nolock(uint32 domain_id, uint32 server_id) { element *elem; - if (!(elem= (element *)my_hash_search(&hash, (const uchar *)&domain_id, 0))) + if (!(elem= (element *)my_hash_search(&hash, (const uchar *)&domain_id, + sizeof(domain_id)))) return NULL; - return (rpl_gtid *)my_hash_search(&elem->hash, (const uchar *)&server_id, 0); + return (rpl_gtid *)my_hash_search(&elem->hash, (const uchar *)&server_id, + sizeof(server_id)); } rpl_gtid * @@ -1913,7 +1926,8 @@ rpl_binlog_state::find_most_recent(uint32 domain_id) rpl_gtid *gtid= NULL; mysql_mutex_lock(&LOCK_binlog_state); - elem= (element *)my_hash_search(&hash, (const uchar *)&domain_id, 0); + elem= (element *)my_hash_search(&hash, (const uchar *)&domain_id, + sizeof(domain_id)); if (elem && elem->last_gtid) gtid= elem->last_gtid; mysql_mutex_unlock(&LOCK_binlog_state); @@ -2182,7 +2196,8 @@ rpl_binlog_state::drop_domain(DYNAMIC_ARRAY *ids, ptr_domain_id= (uint32*) dynamic_array_ptr(ids, i); elem= (rpl_binlog_state::element *) - my_hash_search(&hash, (const uchar *) ptr_domain_id, 0); + my_hash_search(&hash, (const uchar *) ptr_domain_id, + sizeof(ptr_domain_id[0])); if (!elem) { push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, @@ -2243,7 +2258,7 @@ slave_connection_state::slave_connection_state() { my_hash_init(PSI_INSTRUMENT_ME, &hash, &my_charset_bin, 32, offsetof(entry, gtid) + offsetof(rpl_gtid, domain_id), - sizeof(uint32), NULL, my_free, HASH_UNIQUE); + sizeof(rpl_gtid::domain_id), NULL, my_free, HASH_UNIQUE); my_init_dynamic_array(PSI_INSTRUMENT_ME, >id_sort_array, sizeof(rpl_gtid), 8, 8, MYF(0)); } @@ -2298,7 +2313,8 @@ slave_connection_state::load(const char *slave_request, size_t len) return 1; } if ((e= (const entry *) - my_hash_search(&hash, (const uchar *)(>id->domain_id), 0))) + my_hash_search(&hash, (const uchar *)(>id->domain_id), + sizeof(gtid->domain_id)))) { my_error(ER_DUPLICATE_GTID_DOMAIN, MYF(0), gtid->domain_id, gtid->server_id, (ulonglong)gtid->seq_no, e->gtid.domain_id, @@ -2365,7 +2381,8 @@ slave_connection_state::load(rpl_slave_state *state, slave_connection_state::entry * slave_connection_state::find_entry(uint32 domain_id) { - return (entry *) my_hash_search(&hash, (const uchar *)(&domain_id), 0); + return (entry *) my_hash_search(&hash, (const uchar *)(&domain_id), + sizeof(domain_id)); } @@ -2383,7 +2400,8 @@ int slave_connection_state::update(const rpl_gtid *in_gtid) { entry *e; - uchar *rec= my_hash_search(&hash, (const uchar *)(&in_gtid->domain_id), 0); + uchar *rec= my_hash_search(&hash, (const uchar *)(&in_gtid->domain_id), + sizeof(in_gtid->domain_id)); if (rec) { e= (entry *)rec; @@ -2408,7 +2426,8 @@ slave_connection_state::update(const rpl_gtid *in_gtid) void slave_connection_state::remove(const rpl_gtid *in_gtid) { - uchar *rec= my_hash_search(&hash, (const uchar *)(&in_gtid->domain_id), 0); + uchar *rec= my_hash_search(&hash, (const uchar *)(&in_gtid->domain_id), + sizeof(in_gtid->domain_id)); #ifdef DBUG_ASSERT_EXISTS bool err; rpl_gtid *slave_gtid= &((entry *)rec)->gtid; @@ -2425,7 +2444,8 @@ slave_connection_state::remove(const rpl_gtid *in_gtid) void slave_connection_state::remove_if_present(const rpl_gtid *in_gtid) { - uchar *rec= my_hash_search(&hash, (const uchar *)(&in_gtid->domain_id), 0); + uchar *rec= my_hash_search(&hash, (const uchar *)(&in_gtid->domain_id), + sizeof(in_gtid->domain_id)); if (rec) my_hash_delete(&hash, rec); } @@ -2869,7 +2889,8 @@ void gtid_waiting::init() { my_hash_init(PSI_INSTRUMENT_ME, &hash, &my_charset_bin, 32, - offsetof(hash_element, domain_id), sizeof(uint32), NULL, + offsetof(hash_element, domain_id), + sizeof(hash_element::domain_id), NULL, free_hash_element, HASH_UNIQUE); mysql_mutex_init(key_LOCK_gtid_waiting, &LOCK_gtid_waiting, 0); } @@ -2902,7 +2923,8 @@ gtid_waiting::get_entry(uint32 domain_id) { hash_element *e; - if ((e= (hash_element *)my_hash_search(&hash, (const uchar *)&domain_id, 0))) + if ((e= (hash_element *)my_hash_search(&hash, (const uchar *)&domain_id, + sizeof(domain_id)))) return e; if (!(e= (hash_element *)my_malloc(PSI_INSTRUMENT_ME, sizeof(*e), MYF(MY_WME)))) diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index cdd56976549..4fd36891a4b 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -749,7 +749,7 @@ int flush_master_info(Master_info* mi, (1 + mi->ignore_server_ids.elements), MYF(MY_WME)); if (!ignore_server_ids_buf) DBUG_RETURN(1); /* error */ - ulong cur_len= sprintf(ignore_server_ids_buf, "%u", + ulong cur_len= sprintf(ignore_server_ids_buf, "%zu", mi->ignore_server_ids.elements); for (ulong i= 0; i < mi->ignore_server_ids.elements; i++) { @@ -1455,11 +1455,32 @@ bool Master_info_index::add_master_info(Master_info *mi, bool write_to_file) atomic */ -bool Master_info_index::remove_master_info(Master_info *mi) +bool Master_info_index::remove_master_info(Master_info *mi, bool clear_log_files) { + char tmp_name[FN_REFLEN]; DBUG_ENTER("remove_master_info"); mysql_mutex_assert_owner(&LOCK_active_mi); + if (clear_log_files) + { + /* This code is only executed when change_master() failes to create a new master info */ + + // Delete any temporary relay log files that could have been created by change_master() + mi->rli.relay_log.reset_logs(current_thd, 0, (rpl_gtid*) 0, 0, 0); + /* Delete master-'connection'.info */ + create_logfile_name_with_suffix(tmp_name, + sizeof(tmp_name), + master_info_file, 0, + &mi->cmp_connection_name); + my_delete(tmp_name, MYF(0)); + /* Delete relay-log-'connection'.info */ + create_logfile_name_with_suffix(tmp_name, + sizeof(tmp_name), + relay_log_info_file, 0, + &mi->cmp_connection_name); + my_delete(tmp_name, MYF(0)); + } + // Delete Master_info and rewrite others to file if (!my_hash_delete(&master_info_hash, (uchar*) mi)) { @@ -1916,7 +1937,7 @@ char *Domain_id_filter::as_string(enum_list_type type) return NULL; // Store the total number of elements followed by the individual elements. - size_t cur_len= sprintf(buf, "%u", ids->elements); + size_t cur_len= sprintf(buf, "%zu", ids->elements); sz-= cur_len; for (uint i= 0; i < ids->elements; i++) diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h index ce2d3cc9ad5..a4a06d42a5c 100644 --- a/sql/rpl_mi.h +++ b/sql/rpl_mi.h @@ -390,7 +390,7 @@ public: bool check_duplicate_master_info(LEX_CSTRING *connection_name, const char *host, uint port); bool add_master_info(Master_info *mi, bool write_to_file); - bool remove_master_info(Master_info *mi); + bool remove_master_info(Master_info *mi, bool clear_log_files); Master_info *get_master_info(const LEX_CSTRING *connection_name, Sql_condition::enum_warning_level warning); bool start_all_slaves(THD *thd); diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index 8be1964b762..49ec08a9cea 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -807,9 +807,7 @@ do_retry: { mysql_mutex_lock(&entry->LOCK_parallel_entry); if (entry->stop_on_error_sub_id == (uint64) ULONGLONG_MAX || -#ifndef DBUG_OFF - (DBUG_EVALUATE_IF("simulate_mdev_12746", 1, 0)) || -#endif + DBUG_IF("simulate_mdev_12746") || rgi->gtid_sub_id < entry->stop_on_error_sub_id) { register_wait_for_prior_event_group_commit(rgi, entry); @@ -2347,7 +2345,8 @@ rpl_parallel::find(uint32 domain_id) struct rpl_parallel_entry *e; if (!(e= (rpl_parallel_entry *)my_hash_search(&domain_hash, - (const uchar *)&domain_id, 0))) + (const uchar *)&domain_id, + sizeof(domain_id)))) { /* Allocate a new, empty one. */ ulong count= opt_slave_domain_parallel_threads; diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 05ba449d96d..4ba843a51ab 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -1632,7 +1632,8 @@ scan_one_gtid_slave_pos_table(THD *thd, HASH *hash, DYNAMIC_ARRAY *array, goto end; } - if ((rec= my_hash_search(hash, (const uchar *)&domain_id, 0))) + if ((rec= my_hash_search(hash, (const uchar *)&domain_id, + sizeof(domain_id)))) { entry= (struct gtid_pos_element *)rec; if (entry->sub_id >= sub_id) diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc index 9ea8bb3b822..04a2efb3750 100644 --- a/sql/rpl_utility.cc +++ b/sql/rpl_utility.cc @@ -338,7 +338,7 @@ bool event_checksum_test(uchar *event_buf, ulong event_len, DBUG_ASSERT(event_buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT); event_buf[FLAGS_OFFSET]= (uchar) flags; } - res= DBUG_EVALUATE_IF("simulate_checksum_test_failure", TRUE, computed != incoming); + res= (DBUG_IF("simulate_checksum_test_failure") || computed != incoming); } return res; } diff --git a/sql/semisync_master_ack_receiver.cc b/sql/semisync_master_ack_receiver.cc index b65b7824a0e..b54ad58d153 100644 --- a/sql/semisync_master_ack_receiver.cc +++ b/sql/semisync_master_ack_receiver.cc @@ -72,7 +72,7 @@ bool Ack_receiver::start() m_status= ST_UP; - if (DBUG_EVALUATE_IF("rpl_semisync_simulate_create_thread_failure", 1, 0) || + if (DBUG_IF("rpl_semisync_simulate_create_thread_failure") || pthread_attr_init(&attr) != 0 || pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) != 0 || #ifndef _WIN32 @@ -247,7 +247,7 @@ void Ack_receiver::run() { mysql_mutex_unlock(&m_mutex); - ret= DBUG_EVALUATE_IF("rpl_semisync_simulate_select_error", -1, ret); + ret= DBUG_IF("rpl_semisync_simulate_select_error") ? -1 : ret; if (ret == -1 && errno != EINTR) sql_print_information("Failed to wait on semi-sync sockets, " diff --git a/sql/semisync_slave.cc b/sql/semisync_slave.cc index ccfda7576c2..684199031ee 100644 --- a/sql/semisync_slave.cc +++ b/sql/semisync_slave.cc @@ -63,7 +63,7 @@ int Repl_semi_sync_slave::slave_read_sync_header(const uchar *header, if (rpl_semi_sync_slave_status) { - if (DBUG_EVALUATE_IF("semislave_corrupt_log", 0, 1) + if (!DBUG_IF("semislave_corrupt_log") && header[0] == k_packet_magic_num) { semi_sync_need_reply = (header[1] & k_packet_flag_sync); @@ -140,7 +140,7 @@ void Repl_semi_sync_slave::kill_connection(MYSQL *mysql) bool ret= (!mysql_real_connect(kill_mysql, mysql->host, mysql->user, mysql->passwd,0, mysql->port, mysql->unix_socket, 0)); - if (DBUG_EVALUATE_IF("semisync_slave_failed_kill", 1, 0) || ret) + if (DBUG_IF("semisync_slave_failed_kill") || ret) { sql_print_information("cannot connect to master to kill slave io_thread's " "connection"); @@ -172,8 +172,7 @@ int Repl_semi_sync_slave::request_transmit(Master_info *mi) } row= mysql_fetch_row(res); - if (DBUG_EVALUATE_IF("master_not_support_semisync", 1, 0) - || !row) + if (DBUG_IF("master_not_support_semisync") || !row) { /* Master does not support semi-sync */ sql_print_warning("Master server does not support semi-sync, " @@ -234,7 +233,7 @@ int Repl_semi_sync_slave::slave_reply(Master_info *mi) name_len + REPLY_BINLOG_NAME_OFFSET); if (!reply_res) { - reply_res = DBUG_EVALUATE_IF("semislave_failed_net_flush", 1, net_flush(net)); + reply_res = (DBUG_IF("semislave_failed_net_flush") || net_flush(net)); if (reply_res) sql_print_error("Semi-sync slave net_flush() reply failed"); rpl_semi_sync_slave_send_ack++; diff --git a/sql/set_var.cc b/sql/set_var.cc index 3dd97527433..aa9ec5ab5ca 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -85,7 +85,7 @@ uint sys_var_elements() int sys_var_add_options(DYNAMIC_ARRAY *long_options, int parse_flags) { - uint saved_elements= long_options->elements; + size_t saved_elements= long_options->elements; DBUG_ENTER("sys_var_add_options"); diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 31070d04303..2d90793b90f 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -1721,7 +1721,7 @@ ER_TOO_LONG_KEY 42000 S1009 spa "Declaración de clave demasiado larga. La máxima longitud de clave es de %d" swe "För lång nyckel. Högsta tillåtna nyckellängd är %d" ukr "Зазначений ключ задовгий. Найбільша довжина ключа %d байтів" -ER_KEY_COLUMN_DOES_NOT_EXITS 42000 S1009 +ER_KEY_COLUMN_DOES_NOT_EXIST 42000 S1009 cze "Klíčový sloupec '%-.192s' v tabulce neexistuje" dan "Nøglefeltet '%-.192s' eksisterer ikke i tabellen" nla "Zoeksleutel kolom '%-.192s' bestaat niet in tabel" @@ -6127,11 +6127,11 @@ ER_PARTITION_MGMT_ON_NONPARTITIONED ER_FEATURE_NOT_SUPPORTED_WITH_PARTITIONING eng "Partitioned tables do not support %s" spa "Las tablas particionadas no soportan %s" -ER_DROP_PARTITION_NON_EXISTENT - eng "Error in list of partitions to %-.64s" - ger "Fehler in der Partitionsliste bei %-.64s" - spa "Error en lista de particiones para %-.64s" - swe "Fel i listan av partitioner att %-.64s" +ER_PARTITION_DOES_NOT_EXIST + eng "Wrong partition name or partition list" + ger "Falscher Name einer Partition oder Fehler in der Partitionsliste" + spa "Error en lista de particiones" + swe "Fel namn av en partition eller fel i listan av partitioner" ER_DROP_LAST_PARTITION eng "Cannot remove all partitions, use DROP TABLE instead" ger "Es lassen sich nicht sämtliche Partitionen löschen, benutzen Sie statt dessen DROP TABLE" @@ -8907,3 +8907,9 @@ ER_REMOVED_ORPHAN_TRIGGER ER_STORAGE_ENGINE_DISABLED eng "Storage engine %s is disabled" spa "El motor de almacenaje %s está desactivado" +WARN_SFORMAT_ERROR + eng "SFORMAT error: %s" +ER_PARTITION_CONVERT_SUBPARTITIONED + eng "Convert partition is not supported for subpartitioned table." +ER_PROVIDER_NOT_LOADED + eng "MariaDB tried to use the %s, but its provider plugin is not loaded" diff --git a/sql/slave.cc b/sql/slave.cc index 5ff40d8fb25..3c5b830fbe2 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -788,7 +788,7 @@ bool init_slave_skip_errors(const char* arg) if (!arg || !*arg) // No errors defined goto end; - if (unlikely(my_bitmap_init(&slave_error_mask,0,MAX_SLAVE_ERROR,0))) + if (my_bitmap_init(&slave_error_mask,0,MAX_SLAVE_ERROR)) DBUG_RETURN(1); use_slave_mask= 1; @@ -2329,11 +2329,11 @@ past_checksum: /* Announce MariaDB slave capabilities. */ DBUG_EXECUTE_IF("simulate_slave_capability_none", goto after_set_capability;); { - int rc= DBUG_EVALUATE_IF("simulate_slave_capability_old_53", + int rc= DBUG_IF("simulate_slave_capability_old_53") ? mysql_real_query(mysql, STRING_WITH_LEN("SET @mariadb_slave_capability=" - STRINGIFY_ARG(MARIA_SLAVE_CAPABILITY_ANNOTATE))), + STRINGIFY_ARG(MARIA_SLAVE_CAPABILITY_ANNOTATE))) : mysql_real_query(mysql, STRING_WITH_LEN("SET @mariadb_slave_capability=" - STRINGIFY_ARG(MARIA_SLAVE_CAPABILITY_MINE)))); + STRINGIFY_ARG(MARIA_SLAVE_CAPABILITY_MINE))); if (unlikely(rc)) { err_code= mysql_errno(mysql); @@ -4665,7 +4665,7 @@ pthread_handler_t handle_slave_io(void *arg) } thd->variables.wsrep_on= 0; - if (DBUG_EVALUATE_IF("failed_slave_start", 1, 0) + if (DBUG_IF("failed_slave_start") || repl_semisync_slave.slave_start(mi)) { mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL, @@ -4941,7 +4941,7 @@ Stopping slave I/O thread due to out-of-memory error from master"); (!repl_semisync_slave.get_slave_enabled() || (!(mi->semi_ack & SEMI_SYNC_SLAVE_DELAY_SYNC) || (mi->semi_ack & (SEMI_SYNC_NEED_ACK)))) && - (DBUG_EVALUATE_IF("failed_flush_master_info", 1, 0) || + (DBUG_IF("failed_flush_master_info") || flush_master_info(mi, TRUE, TRUE))) { sql_print_error("Failed to flush master info file"); diff --git a/sql/sp_head.h b/sql/sp_head.h index 601d41ab04a..475f1e0d424 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -561,7 +561,7 @@ public: { return m_flags & MODIFIES_DATA; } inline uint instructions() - { return m_instr.elements; } + { return (uint)m_instr.elements; } inline sp_instr * last_instruction() diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index c4c19dd39f6..619218f3b68 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -518,7 +518,8 @@ bool sp_rcontext::handle_sql_condition(THD *thd, found_condition= new (callers_arena->mem_root) Sql_condition(callers_arena->mem_root, da->get_error_condition_identity(), - da->message()); + da->message(), + da->current_row_for_warning()); } } else if (da->current_statement_warn_count()) diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 0e0e8921f86..ea669b2d1d8 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -111,15 +111,18 @@ public: /// Text message. char *message; + /** Row number where the condition has happened */ + ulong m_row_number; + /// The constructor. /// /// @param _sql_condition The SQL condition. /// @param arena Query arena for SP - Sql_condition_info(const Sql_condition *_sql_condition, - Query_arena *arena) + Sql_condition_info(const Sql_condition *_sql_condition, Query_arena *arena) :Sql_condition_identity(*_sql_condition) { message= strdup_root(arena->mem_root, _sql_condition->get_message_text()); + m_row_number= _sql_condition->m_row_number; } }; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 9c71f8b7149..2d7f62cd725 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2168,24 +2168,26 @@ static bool has_validation_plugins() MariaDB_PASSWORD_VALIDATION_PLUGIN, NULL); } -struct validation_data { const LEX_CSTRING *user, *password; }; +struct validation_data { const LEX_CSTRING *user, *password, *host; }; static my_bool do_validate(THD *, plugin_ref plugin, void *arg) { struct validation_data *data= (struct validation_data *)arg; struct st_mariadb_password_validation *handler= (st_mariadb_password_validation *)plugin_decl(plugin)->info; - return handler->validate_password(data->user, data->password); + return handler->validate_password(data->user, data->password, data->host); } static bool validate_password(THD *thd, const LEX_CSTRING &user, + const LEX_CSTRING &host, const LEX_CSTRING &pwtext, bool has_hash) { if (pwtext.length || !has_hash) { struct validation_data data= { &user, - pwtext.str ? &pwtext : &empty_clex_str }; + pwtext.str ? &pwtext : &empty_clex_str, + &host }; if (plugin_foreach(NULL, do_validate, MariaDB_PASSWORD_VALIDATION_PLUGIN, &data)) { @@ -2239,6 +2241,7 @@ static int set_user_salt(ACL_USER::AUTH *auth, plugin_ref plugin) not loaded, if the auth_string is invalid, if the password is not applicable */ static int set_user_auth(THD *thd, const LEX_CSTRING &user, + const LEX_CSTRING &host, ACL_USER::AUTH *auth, const LEX_CSTRING &pwtext) { const char *plugin_name= auth->plugin.str; @@ -2264,7 +2267,7 @@ static int set_user_auth(THD *thd, const LEX_CSTRING &user, } if (info->hash_password && - validate_password(thd, user, pwtext, auth->auth_string.length)) + validate_password(thd, user, host, pwtext, auth->auth_string.length)) { res= ER_NOT_VALID_PASSWORD; goto end; @@ -3374,7 +3377,9 @@ static int acl_user_update(THD *thd, ACL_USER *acl_user, uint nauth, auth->auth_str); if (fix_user_plugin_ptr(work_copy + i)) work_copy[i].plugin= safe_lexcstrdup_root(&acl_memroot, auth->plugin); - if (set_user_auth(thd, acl_user->user, work_copy + i, auth->pwtext)) + if (set_user_auth(thd, acl_user->user, + {acl_user->host.hostname, acl_user->hostname_length}, + work_copy + i, auth->pwtext)) return 1; } } @@ -3642,14 +3647,14 @@ static void init_check_host(void) (my_hash_get_key) check_get_key, 0, 0); if (!allow_all_hosts) { - for (uint i=0 ; i < acl_users.elements ; i++) + for (size_t i=0 ; i < acl_users.elements ; i++) { ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); if (strchr(acl_user->host.hostname,wild_many) || strchr(acl_user->host.hostname,wild_one) || acl_user->host.ip_mask) { // Has wildcard - uint j; + size_t j; for (j=0 ; j < acl_wild_hosts.elements ; j++) { // Check if host already exists acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,j, @@ -3768,7 +3773,7 @@ static bool add_role_user_mapping(const char *uname, const char *hname, static void remove_ptr_from_dynarray(DYNAMIC_ARRAY *array, void *ptr) { bool found __attribute__((unused))= false; - for (uint i= 0; i < array->elements; i++) + for (size_t i= 0; i < array->elements; i++) { if (ptr == *dynamic_element(array, i, void**)) { @@ -3817,7 +3822,7 @@ static void rebuild_role_grants(void) /* Reset every user's and role's role_grants array */ - for (uint i=0; i < acl_users.elements; i++) { + for (size_t i=0; i < acl_users.elements; i++) { ACL_USER *user= dynamic_element(&acl_users, i, ACL_USER *); reset_dynamic(&user->role_grants); } @@ -3843,7 +3848,7 @@ bool acl_check_host(const char *host, const char *ip) mysql_mutex_unlock(&acl_cache->lock); return 0; // Found host } - for (uint i=0 ; i < acl_wild_hosts.elements ; i++) + for (size_t i=0 ; i < acl_wild_hosts.elements ; i++) { acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,i,acl_host_and_ip*); if (compare_hostname(acl, host, ip)) @@ -4000,7 +4005,8 @@ bool change_password(THD *thd, LEX_USER *user) { auth= acl_user->auth[i]; auth.auth_string= safe_lexcstrdup_root(&acl_memroot, user->auth->auth_str); - int r= set_user_auth(thd, user->user, &auth, user->auth->pwtext); + int r= set_user_auth(thd, user->user, user->host, + &auth, user->auth->pwtext); if (r == ER_SET_PASSWORD_AUTH_PLUGIN) password_plugin= auth.plugin.str; else if (r) @@ -4283,14 +4289,18 @@ static ACL_USER * find_user_wild(const char *host, const char *user, const char */ static ACL_ROLE *find_acl_role(const char *role) { + size_t length= strlen(role); DBUG_ENTER("find_acl_role"); DBUG_PRINT("enter",("role: '%s'", role)); DBUG_PRINT("info", ("Hash elements: %ld", acl_roles.records)); mysql_mutex_assert_owner(&acl_cache->lock); + if (!length) + DBUG_RETURN(NULL); + ACL_ROLE *r= (ACL_ROLE *)my_hash_search(&acl_roles, (uchar *)role, - strlen(role)); + length); DBUG_RETURN(r); } @@ -5042,7 +5052,7 @@ acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke) mysql_mutex_assert_owner(&acl_cache->lock); DBUG_ENTER("acl_update_proxy_user"); - for (uint i= 0; i < acl_proxy_users.elements; i++) + for (size_t i= 0; i < acl_proxy_users.elements; i++) { ACL_PROXY_USER *acl_user= dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *); @@ -6314,7 +6324,7 @@ static int traverse_role_graph_impl(ACL_USER_BASE *user, void *context, end: /* Cleanup */ - for (uint i= 0; i < to_clear.elements(); i++) + for (size_t i= 0; i < to_clear.elements(); i++) { ACL_USER_BASE *current= to_clear.at(i); DBUG_ASSERT(current->flags & (ROLE_EXPLORED | ROLE_ON_STACK | ROLE_OPENED)); @@ -6382,7 +6392,7 @@ static bool merge_role_global_privileges(ACL_ROLE *grantee) DBUG_EXECUTE_IF("role_merge_stats", role_global_merges++;); - for (uint i= 0; i < grantee->role_grants.elements; i++) + for (size_t i= 0; i < grantee->role_grants.elements; i++) { ACL_ROLE *r= *dynamic_element(&grantee->role_grants, i, ACL_ROLE**); grantee->access|= r->access; @@ -6521,8 +6531,8 @@ static bool merge_role_db_privileges(ACL_ROLE *grantee, const char *dbname, if (update_flags & 4) { // Remove elements marked for deletion. - uint count= 0; - for(uint i= 0; i < acl_dbs.elements(); i++) + size_t count= 0; + for(size_t i= 0; i < acl_dbs.elements(); i++) { ACL_DB *acl_db= &acl_dbs.at(i); if (acl_db->sort) @@ -6885,7 +6895,7 @@ static int merge_role_privileges(ACL_ROLE *role __attribute__((unused)), if (data->what != PRIVS_TO_MERGE::GLOBAL) { role_hash.insert(grantee); - for (uint i= 0; i < grantee->role_grants.elements; i++) + for (size_t i= 0; i < grantee->role_grants.elements; i++) role_hash.insert(*dynamic_element(&grantee->role_grants, i, ACL_ROLE**)); } @@ -9460,7 +9470,7 @@ static bool show_role_grants(THD *thd, const char *hostname, ACL_USER_BASE *acl_entry, char *buff, size_t buffsize) { - uint counter; + size_t counter; Protocol *protocol= thd->protocol; LEX_CSTRING host= {const_cast<char*>(hostname), strlen(hostname)}; @@ -9573,7 +9583,7 @@ static bool show_database_privileges(THD *thd, const char *username, privilege_t want_access(NO_ACL); Protocol *protocol= thd->protocol; - for (uint i=0 ; i < acl_dbs.elements() ; i++) + for (size_t i=0 ; i < acl_dbs.elements() ; i++) { const char *user, *host; @@ -10265,14 +10275,14 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, propagate_role_grants(acl_role, PRIVS_TO_MERGE::ALL); // delete the role from cross-reference arrays - for (uint i=0; i < acl_role->role_grants.elements; i++) + for (size_t i=0; i < acl_role->role_grants.elements; i++) { ACL_ROLE *grant= *dynamic_element(&acl_role->role_grants, i, ACL_ROLE**); remove_ptr_from_dynarray(&grant->parent_grantee, acl_role); } - for (uint i=0; i < acl_role->parent_grantee.elements; i++) + for (size_t i=0; i < acl_role->parent_grantee.elements; i++) { ACL_USER_BASE *grantee= *dynamic_element(&acl_role->parent_grantee, i, ACL_USER_BASE**); @@ -10293,7 +10303,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, /* Get the number of elements in the in-memory structure. */ switch (struct_no) { case USER_ACL: - elements= acl_users.elements; + elements= int(acl_users.elements); break; case DB_ACL: elements= int(acl_dbs.elements()); @@ -10319,7 +10329,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, elements= grant_name_hash->records; break; case PROXY_USERS_ACL: - elements= acl_proxy_users.elements; + elements= int(acl_proxy_users.elements); break; case ROLES_MAPPINGS_HASH: roles_mappings_hash= &acl_roles_mappings; @@ -12182,11 +12192,11 @@ SHOW_VAR acl_statistics[] = { {"procedure_grants", (char*)&proc_priv_hash.records, SHOW_ULONG}, {"package_spec_grants", (char*)&package_spec_priv_hash.records, SHOW_ULONG}, {"package_body_grants", (char*)&package_body_priv_hash.records, SHOW_ULONG}, - {"proxy_users", (char*)&acl_proxy_users.elements, SHOW_UINT}, + {"proxy_users", (char*)&acl_proxy_users.elements, SHOW_SIZE_T}, {"role_grants", (char*)&acl_roles_mappings.records, SHOW_ULONG}, {"roles", (char*)&acl_roles.records, SHOW_ULONG}, {"table_grants", (char*)&column_priv_hash.records, SHOW_ULONG}, - {"users", (char*)&acl_users.elements, SHOW_UINT}, + {"users", (char*)&acl_users.elements, SHOW_SIZE_T}, #endif {NullS, NullS, SHOW_LONG}, }; diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 6ba08ebd55a..26e3d67641d 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -713,8 +713,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, protocol->store(operator_name, system_charset_info); protocol->store(&error_clex_str, system_charset_info); length= my_snprintf(buff, sizeof(buff), - ER_THD(thd, ER_DROP_PARTITION_NON_EXISTENT), - table_name.str); + ER_THD(thd, ER_PARTITION_DOES_NOT_EXIST)); protocol->store(buff, length, system_charset_info); if(protocol->write()) goto err; diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index be305267076..e28308b4ff6 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -427,6 +427,8 @@ bool Sql_cmd_alter_table::execute(THD *thd) as for RENAME TO, as being done by SQLCOM_RENAME_TABLE */ if ((alter_info.partition_flags & ALTER_PARTITION_DROP) || + (alter_info.partition_flags & ALTER_PARTITION_CONVERT_IN) || + (alter_info.partition_flags & ALTER_PARTITION_CONVERT_OUT) || (alter_info.flags & ALTER_RENAME)) priv_needed|= DROP_ACL; diff --git a/sql/sql_alter.h b/sql/sql_alter.h index d91984d4b26..bf1edd4c964 100644 --- a/sql/sql_alter.h +++ b/sql/sql_alter.h @@ -250,13 +250,15 @@ public: const LEX_CSTRING *new_db_arg, const LEX_CSTRING *new_name_arg); /** - @return true if the table is moved to another database, false otherwise. + @return true if the table is moved to another database or a new table + created by ALTER_PARTITION_CONVERT_OUT, false otherwise. */ bool is_database_changed() const { return (new_db.str != db.str); }; /** - @return true if the table is renamed, false otherwise. + @return true if the table is renamed or a new table created by + ALTER_PARTITION_CONVERT_OUT, false otherwise. */ bool is_table_renamed() const { return (is_database_changed() || new_name.str != table_name.str); }; diff --git a/sql/sql_array.h b/sql/sql_array.h index 8610e971016..85a53ae1a6f 100644 --- a/sql/sql_array.h +++ b/sql/sql_array.h @@ -114,19 +114,19 @@ template <class Elem> class Dynamic_array { DYNAMIC_ARRAY array; public: - Dynamic_array(PSI_memory_key psi_key, uint prealloc=16, uint increment=16) + Dynamic_array(PSI_memory_key psi_key, size_t prealloc=16, size_t increment=16) { init(psi_key, prealloc, increment); } - Dynamic_array(MEM_ROOT *root, uint prealloc=16, uint increment=16) + Dynamic_array(MEM_ROOT *root, size_t prealloc=16, size_t increment=16) { void *init_buffer= alloc_root(root, sizeof(Elem) * prealloc); - init_dynamic_array2(root->m_psi_key, &array, sizeof(Elem), init_buffer, - prealloc, increment, MYF(0)); + init_dynamic_array2(root->psi_key, &array, sizeof(Elem), init_buffer, + prealloc, increment, MYF(0)); } - void init(PSI_memory_key psi_key, uint prealloc=16, uint increment=16) + void init(PSI_memory_key psi_key, size_t prealloc=16, size_t increment=16) { init_dynamic_array2(psi_key, &array, sizeof(Elem), 0, prealloc, increment, MYF(0)); } @@ -217,7 +217,7 @@ public: void del(size_t idx) { DBUG_ASSERT(idx <= array.max_element); - delete_dynamic_element(&array, (uint)idx); + delete_dynamic_element(&array, idx); } size_t elements() const @@ -228,7 +228,7 @@ public: void elements(size_t num_elements) { DBUG_ASSERT(num_elements <= array.max_element); - array.elements= (uint)num_elements; + array.elements= num_elements; } void clear() @@ -236,7 +236,7 @@ public: elements(0); } - void set(uint idx, const Elem &el) + void set(size_t idx, const Elem &el) { set_dynamic(&array, &el, idx); } @@ -248,7 +248,7 @@ public: bool reserve(size_t new_size) { - return allocate_dynamic(&array, (uint)new_size); + return allocate_dynamic(&array, new_size); } @@ -260,7 +260,7 @@ public: if (new_size > old_size) { - set_dynamic(&array, (uchar*)&default_val, (uint)(new_size - 1)); + set_dynamic(&array, (uchar*)&default_val, new_size - 1); /*for (size_t i= old_size; i != new_size; i++) { at(i)= default_val; diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc index 3e9379ebe33..cbf3f971088 100644 --- a/sql/sql_audit.cc +++ b/sql/sql_audit.cc @@ -350,14 +350,11 @@ static my_bool calc_class_mask(THD *thd, plugin_ref plugin, void *arg) */ int finalize_audit_plugin(st_plugin_int *plugin) { + int deinit_status= 0; unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE]; - if (plugin->plugin->deinit && plugin->plugin->deinit(NULL)) - { - DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.", - plugin->name.str)); - DBUG_EXECUTE("finalize_audit_plugin", return 1; ); - } + if (plugin->plugin->deinit) + deinit_status= plugin->plugin->deinit(NULL); plugin->data= NULL; bzero(&event_class_mask, sizeof(event_class_mask)); @@ -376,7 +373,7 @@ int finalize_audit_plugin(st_plugin_int *plugin) bmove(mysql_global_audit_mask, event_class_mask, sizeof(event_class_mask)); mysql_mutex_unlock(&LOCK_audit_mask); - return 0; + return deinit_status; } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 8d213ce103b..414bb5efa25 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -6047,7 +6047,7 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, size_t length, if (field) { if (field->invisible == INVISIBLE_FULL && - DBUG_EVALUATE_IF("test_completely_invisible", 0, 1)) + !DBUG_IF("test_completely_invisible")) DBUG_RETURN((Field*)0); if (field->invisible == INVISIBLE_SYSTEM && @@ -7440,7 +7440,7 @@ store_top_level_join_columns(THD *thd, TABLE_LIST *table_ref, /* Add a TRUE condition to outer joins that have no common columns. */ if (table_ref_2->outer_join && !table_ref_1->on_expr && !table_ref_2->on_expr) - table_ref_2->on_expr= (Item*) &Item_true; + table_ref_2->on_expr= (Item*) Item_true; /* Change this table reference to become a leaf for name resolution. */ if (left_neighbor) @@ -8936,7 +8936,7 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, Field **ptr, my_bool mysql_rm_tmp_tables(void) { - uint i, idx; + size_t i, idx; char path[FN_REFLEN], *tmpdir, path_copy[FN_REFLEN]; MY_DIR *dirp; FILEINFO *file; @@ -8958,7 +8958,7 @@ my_bool mysql_rm_tmp_tables(void) /* Remove all SQLxxx tables from directory */ - for (idx=0 ; idx < (uint) dirp->number_of_files ; idx++) + for (idx=0 ; idx < dirp->number_of_files ; idx++) { file=dirp->dir_entry+idx; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 6b7c4442204..29824301e9d 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -293,7 +293,7 @@ bool Foreign_key::validate(List<Create_field> &table_fields) &sql_field->field_name)) {} if (!sql_field) { - my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str); + my_error(ER_KEY_COLUMN_DOES_NOT_EXIST, MYF(0), column->field_name.str); DBUG_RETURN(TRUE); } if (type == Key::FOREIGN_KEY && sql_field->vcol_info) @@ -748,8 +748,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) will be re-initialized in init_for_queries(). */ init_sql_alloc(key_memory_thd_main_mem_root, - &main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0, - MYF(MY_THREAD_SPECIFIC)); + &main_mem_root, 64, 0, MYF(MY_THREAD_SPECIFIC)); /* Allocation of user variables for binary logging is always done with main @@ -966,10 +965,8 @@ Internal_error_handler *THD::pop_internal_handler() void THD::raise_error(uint sql_errno) { const char* msg= ER_THD(this, sql_errno); - (void) raise_condition(sql_errno, - NULL, - Sql_condition::WARN_LEVEL_ERROR, - msg); + (void) raise_condition(sql_errno, "\0\0\0\0\0", + Sql_condition::WARN_LEVEL_ERROR, msg); } void THD::raise_error_printf(uint sql_errno, ...) @@ -982,20 +979,16 @@ void THD::raise_error_printf(uint sql_errno, ...) va_start(args, sql_errno); my_vsnprintf(ebuff, sizeof(ebuff), format, args); va_end(args); - (void) raise_condition(sql_errno, - NULL, - Sql_condition::WARN_LEVEL_ERROR, - ebuff); + (void) raise_condition(sql_errno, "\0\0\0\0\0", + Sql_condition::WARN_LEVEL_ERROR, ebuff); DBUG_VOID_RETURN; } void THD::raise_warning(uint sql_errno) { const char* msg= ER_THD(this, sql_errno); - (void) raise_condition(sql_errno, - NULL, - Sql_condition::WARN_LEVEL_WARN, - msg); + (void) raise_condition(sql_errno, "\0\0\0\0\0", + Sql_condition::WARN_LEVEL_WARN, msg); } void THD::raise_warning_printf(uint sql_errno, ...) @@ -1008,10 +1001,8 @@ void THD::raise_warning_printf(uint sql_errno, ...) va_start(args, sql_errno); my_vsnprintf(ebuff, sizeof(ebuff), format, args); va_end(args); - (void) raise_condition(sql_errno, - NULL, - Sql_condition::WARN_LEVEL_WARN, - ebuff); + (void) raise_condition(sql_errno, "\0\0\0\0\0", + Sql_condition::WARN_LEVEL_WARN, ebuff); DBUG_VOID_RETURN; } @@ -1022,10 +1013,8 @@ void THD::raise_note(uint sql_errno) if (!(variables.option_bits & OPTION_SQL_NOTES)) DBUG_VOID_RETURN; const char* msg= ER_THD(this, sql_errno); - (void) raise_condition(sql_errno, - NULL, - Sql_condition::WARN_LEVEL_NOTE, - msg); + (void) raise_condition(sql_errno, "\0\0\0\0\0", + Sql_condition::WARN_LEVEL_NOTE, msg); DBUG_VOID_RETURN; } @@ -1041,21 +1030,20 @@ void THD::raise_note_printf(uint sql_errno, ...) va_start(args, sql_errno); my_vsnprintf(ebuff, sizeof(ebuff), format, args); va_end(args); - (void) raise_condition(sql_errno, - NULL, - Sql_condition::WARN_LEVEL_NOTE, - ebuff); + (void) raise_condition(sql_errno, "\0\0\0\0\0", + Sql_condition::WARN_LEVEL_NOTE, ebuff); DBUG_VOID_RETURN; } -Sql_condition* THD::raise_condition(uint sql_errno, - const char* sqlstate, - Sql_condition::enum_warning_level level, - const Sql_user_condition_identity &ucid, - const char* msg) +Sql_condition* THD::raise_condition(const Sql_condition *cond) { + uint sql_errno= cond->get_sql_errno(); + const char *sqlstate= cond->get_sqlstate(); + Sql_condition::enum_warning_level level= cond->get_level(); + const char *msg= cond->get_message_text(); + Diagnostics_area *da= get_stmt_da(); - Sql_condition *cond= NULL; + Sql_condition *raised= NULL; DBUG_ENTER("THD::raise_condition"); DBUG_ASSERT(level < Sql_condition::WARN_LEVEL_END); @@ -1083,22 +1071,18 @@ Sql_condition* THD::raise_condition(uint sql_errno, sql_errno= ER_UNKNOWN_ERROR; if (msg == NULL) msg= ER_THD(this, sql_errno); - if (sqlstate == NULL) + if (!*sqlstate) sqlstate= mysql_errno_to_sqlstate(sql_errno); - if ((level == Sql_condition::WARN_LEVEL_WARN) && - really_abort_on_warning()) + if ((level == Sql_condition::WARN_LEVEL_WARN) && really_abort_on_warning()) { - /* - FIXME: - push_warning and strict SQL_MODE case. - */ + /* FIXME: push_warning and strict SQL_MODE case. */ level= Sql_condition::WARN_LEVEL_ERROR; } if (!is_fatal_error && - handle_condition(sql_errno, sqlstate, &level, msg, &cond)) - DBUG_RETURN(cond); + handle_condition(sql_errno, sqlstate, &level, msg, &raised)) + goto ret; switch (level) { case Sql_condition::WARN_LEVEL_NOTE: @@ -1123,8 +1107,7 @@ Sql_condition* THD::raise_condition(uint sql_errno, With wsrep we allow converting BF abort error to warning if errors are ignored. */ - if (!is_fatal_error && - no_errors && + if (!is_fatal_error && no_errors && (wsrep_trx().bf_aborted() || wsrep_retry_counter)) { WSREP_DEBUG("BF abort error converted to warning"); @@ -1135,7 +1118,7 @@ Sql_condition* THD::raise_condition(uint sql_errno, if (!da->is_error()) { set_row_count_func(-1); - da->set_error_status(sql_errno, msg, sqlstate, ucid, cond); + da->set_error_status(sql_errno, msg, sqlstate, *cond, raised); } } } @@ -1150,9 +1133,13 @@ Sql_condition* THD::raise_condition(uint sql_errno, if (likely(!(is_fatal_error && (sql_errno == EE_OUTOFMEMORY || sql_errno == ER_OUTOFMEMORY)))) { - cond= da->push_warning(this, sql_errno, sqlstate, level, ucid, msg); + raised= da->push_warning(this, sql_errno, sqlstate, level, *cond, msg, + cond->m_row_number); } - DBUG_RETURN(cond); +ret: + if (raised) + raised->copy_opt_attributes(cond); + DBUG_RETURN(raised); } extern "C" diff --git a/sql/sql_class.h b/sql/sql_class.h index f5a0d6abc9e..df9d89b5aff 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -560,7 +560,8 @@ typedef enum enum_diag_condition_item_name DIAG_CURSOR_NAME= 9, DIAG_MESSAGE_TEXT= 10, DIAG_MYSQL_ERRNO= 11, - LAST_DIAG_SET_PROPERTY= DIAG_MYSQL_ERRNO + DIAG_ROW_NUMBER= 12, + LAST_DIAG_SET_PROPERTY= DIAG_ROW_NUMBER } Diag_condition_item_name; /** @@ -1131,6 +1132,7 @@ struct THD_count { static Atomic_counter<uint32_t> count; static uint value() { return static_cast<uint>(count); } + static uint connection_thd_count(); THD_count() { count++; } ~THD_count() { count--; } }; @@ -3049,8 +3051,8 @@ public: { bzero((char*)this, sizeof(*this)); implicit_xid.null(); - init_sql_alloc(key_memory_thd_transactions, &mem_root, - ALLOC_ROOT_MIN_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC)); + init_sql_alloc(key_memory_thd_transactions, &mem_root, 256, + 0, MYF(MY_THREAD_SPECIFIC)); } } default_transaction, *transaction; Global_read_lock global_read_lock; @@ -3918,6 +3920,11 @@ public: user_time= t; set_time(); } + inline void force_set_time(my_time_t t, ulong sec_part) + { + start_time= system_time.sec= t; + start_time_sec_part= system_time.sec_part= sec_part; + } /* this is only used by replication and BINLOG command. usecs > TIME_MAX_SECOND_PART means "was not in binlog" @@ -3929,15 +3936,9 @@ public: else { if (sec_part <= TIME_MAX_SECOND_PART) - { - start_time= system_time.sec= t; - start_time_sec_part= system_time.sec_part= sec_part; - } + force_set_time(t, sec_part); else if (t != system_time.sec) - { - start_time= system_time.sec= t; - start_time_sec_part= system_time.sec_part= 0; - } + force_set_time(t, 0); else { start_time= t; @@ -4800,45 +4801,17 @@ private: @param msg the condition message text @return The condition raised, or NULL */ - Sql_condition* - raise_condition(uint sql_errno, - const char* sqlstate, - Sql_condition::enum_warning_level level, - const char* msg) + Sql_condition* raise_condition(uint sql_errno, const char* sqlstate, + Sql_condition::enum_warning_level level, const char* msg) { - return raise_condition(sql_errno, sqlstate, level, - Sql_user_condition_identity(), msg); + Sql_condition cond(NULL, // don't strdup the msg + Sql_condition_identity(sql_errno, sqlstate, level, + Sql_user_condition_identity()), + msg, get_stmt_da()->current_row_for_warning()); + return raise_condition(&cond); } - /** - Raise a generic or a user defined SQL condition. - @param ucid - the user condition identity - (or an empty identity if not a user condition) - @param sql_errno - the condition error number - @param sqlstate - the condition SQLSTATE - @param level - the condition level - @param msg - the condition message text - @return The condition raised, or NULL - */ - Sql_condition* - raise_condition(uint sql_errno, - const char* sqlstate, - Sql_condition::enum_warning_level level, - const Sql_user_condition_identity &ucid, - const char* msg); - - Sql_condition* - raise_condition(const Sql_condition *cond) - { - Sql_condition *raised= raise_condition(cond->get_sql_errno(), - cond->get_sqlstate(), - cond->get_level(), - *cond/*Sql_user_condition_identity*/, - cond->get_message_text()); - if (raised) - raised->copy_opt_attributes(cond); - return raised; - } + Sql_condition* raise_condition(const Sql_condition *cond); private: void push_warning_truncated_priv(Sql_condition::enum_warning_level level, diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h index ce34852117f..430afadb491 100644 --- a/sql/sql_cmd.h +++ b/sql/sql_cmd.h @@ -75,7 +75,6 @@ enum enum_sql_command { SQLCOM_XA_START, SQLCOM_XA_END, SQLCOM_XA_PREPARE, SQLCOM_XA_COMMIT, SQLCOM_XA_ROLLBACK, SQLCOM_XA_RECOVER, SQLCOM_SHOW_PROC_CODE, SQLCOM_SHOW_FUNC_CODE, - SQLCOM_ALTER_TABLESPACE, SQLCOM_INSTALL_PLUGIN, SQLCOM_UNINSTALL_PLUGIN, SQLCOM_SHOW_AUTHORS, SQLCOM_BINLOG_BASE64_EVENT, SQLCOM_SHOW_PLUGINS, SQLCOM_SHOW_CONTRIBUTORS, diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 37e136927f2..c5defc1959c 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -1363,9 +1363,7 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp, *tables= tot_list; /* and at last delete all non-table files */ - for (uint idx=0 ; - idx < (uint) dirp->number_of_files && !thd->killed ; - idx++) + for (size_t idx=0; idx < dirp->number_of_files && !thd->killed; idx++) { FILEINFO *file=dirp->dir_entry+idx; char *extension; @@ -1488,9 +1486,7 @@ long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path) DBUG_ENTER("mysql_rm_arc_files"); DBUG_PRINT("enter", ("path: %s", org_path)); - for (uint idx=0 ; - idx < (uint) dirp->number_of_files && !thd->killed ; - idx++) + for (size_t idx=0; idx < dirp->number_of_files && !thd->killed; idx++) { FILEINFO *file=dirp->dir_entry+idx; char *extension, *revision; @@ -1970,8 +1966,8 @@ bool mysql_upgrade_db(THD *thd, const LEX_CSTRING *old_db) /* Step2: Move tables to the new database */ if ((dirp = my_dir(path,MYF(MY_DONT_SORT)))) { - uint nfiles= (uint) dirp->number_of_files; - for (uint idx=0 ; idx < nfiles && !thd->killed ; idx++) + size_t nfiles= dirp->number_of_files; + for (size_t idx=0 ; idx < nfiles && !thd->killed ; idx++) { FILEINFO *file= dirp->dir_entry + idx; char *extension, tname[FN_REFLEN + 1]; @@ -2060,8 +2056,8 @@ bool mysql_upgrade_db(THD *thd, const LEX_CSTRING *old_db) if ((dirp = my_dir(path,MYF(MY_DONT_SORT)))) { - uint nfiles= (uint) dirp->number_of_files; - for (uint idx=0 ; idx < nfiles ; idx++) + size_t nfiles= dirp->number_of_files; + for (size_t idx=0 ; idx < nfiles ; idx++) { FILEINFO *file= dirp->dir_entry + idx; char oldname[FN_REFLEN + 1], newname[FN_REFLEN + 1]; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index e35cf17b182..0403d6e73c3 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -728,6 +728,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, explain= (Explain_delete*)thd->lex->explain->get_upd_del_plan(); explain->tracker.on_scan_init(); + thd->get_stmt_da()->reset_current_row_for_warning(1); + if (!delete_while_scanning) { /* @@ -793,9 +795,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, THD_STAGE_INFO(thd, stage_updating); fix_rownum_pointers(thd, thd->lex->current_select, &deleted); + thd->get_stmt_da()->reset_current_row_for_warning(0); while (likely(!(error=info.read_record())) && likely(!thd->killed) && likely(!thd->is_error())) { + thd->get_stmt_da()->inc_current_row_for_warning(); if (delete_while_scanning) delete_record= record_should_be_deleted(thd, table, select, explain, delete_history); @@ -871,6 +875,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, else break; } + thd->get_stmt_da()->reset_current_row_for_warning(1); terminate_delete: killed_status= thd->killed; diff --git a/sql/sql_error.cc b/sql/sql_error.cc index cef9e6cec00..85be61c34ef 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -204,6 +204,7 @@ Sql_condition::copy_opt_attributes(const Sql_condition *cond) copy_string(m_mem_root, & m_table_name, & cond->m_table_name); copy_string(m_mem_root, & m_column_name, & cond->m_column_name); copy_string(m_mem_root, & m_cursor_name, & cond->m_cursor_name); + m_row_number= cond->m_row_number; } @@ -216,7 +217,7 @@ Sql_condition::set_builtin_message_text(const char* str) */ const char* copy; - copy= strdup_root(m_mem_root, str); + copy= m_mem_root ? strdup_root(m_mem_root, str) : str; m_message_text.set(copy, strlen(copy), error_message_charset_info); DBUG_ASSERT(! m_message_text.is_alloced()); } @@ -500,7 +501,7 @@ Diagnostics_area::disable_status() Warning_info::Warning_info(ulonglong warn_id_arg, bool allow_unlimited_warnings, bool initialize) :m_current_statement_warn_count(0), - m_current_row_for_warning(1), + m_current_row_for_warning(0), m_warn_id(warn_id_arg), m_error_condition(NULL), m_allow_unlimited_warnings(allow_unlimited_warnings), @@ -557,7 +558,7 @@ void Warning_info::clear(ulonglong new_id) free_memory(); memset(m_warn_count, 0, sizeof(m_warn_count)); m_current_statement_warn_count= 0; - m_current_row_for_warning= 1; /* Start counting from the first row */ + m_current_row_for_warning= 0; clear_error_condition(); } @@ -663,7 +664,8 @@ void Warning_info::reserve_space(THD *thd, uint count) Sql_condition *Warning_info::push_warning(THD *thd, const Sql_condition_identity *value, - const char *msg) + const char *msg, + ulong current_row_number) { Sql_condition *cond= NULL; @@ -672,7 +674,8 @@ Sql_condition *Warning_info::push_warning(THD *thd, if (m_allow_unlimited_warnings || m_warn_list.elements() < thd->variables.max_error_count) { - cond= new (& m_warn_root) Sql_condition(& m_warn_root, *value, msg); + cond= new (& m_warn_root) Sql_condition(& m_warn_root, *value, msg, + current_row_number); if (cond) m_warn_list.push_back(cond); } @@ -688,7 +691,8 @@ Sql_condition *Warning_info::push_warning(THD *thd, const Sql_condition *sql_condition) { Sql_condition *new_condition= push_warning(thd, sql_condition, - sql_condition->get_message_text()); + sql_condition->get_message_text(), + sql_condition->m_row_number); if (new_condition) new_condition->copy_opt_attributes(sql_condition); @@ -723,7 +727,7 @@ void push_warning(THD *thd, Sql_condition::enum_warning_level level, if (level == Sql_condition::WARN_LEVEL_ERROR) level= Sql_condition::WARN_LEVEL_WARN; - (void) thd->raise_condition(code, NULL, level, msg); + (void) thd->raise_condition(code, "\0\0\0\0\0", level, msg); /* Make sure we also count warnings pushed after calling set_ok_status(). */ thd->get_stmt_da()->increment_warning(); diff --git a/sql/sql_error.h b/sql/sql_error.h index 6b0d4d7749c..d9d7f420baa 100644 --- a/sql/sql_error.h +++ b/sql/sql_error.h @@ -306,6 +306,9 @@ protected: /** SQL CURSOR_NAME condition item. */ String m_cursor_name; + /** SQL ROW_NUMBER condition item. */ + ulong m_row_number; + Sql_condition_items() :m_class_origin((const char*) NULL, 0, & my_charset_utf8mb3_bin), m_subclass_origin((const char*) NULL, 0, & my_charset_utf8mb3_bin), @@ -316,7 +319,8 @@ protected: m_schema_name((const char*) NULL, 0, & my_charset_utf8mb3_bin), m_table_name((const char*) NULL, 0, & my_charset_utf8mb3_bin), m_column_name((const char*) NULL, 0, & my_charset_utf8mb3_bin), - m_cursor_name((const char*) NULL, 0, & my_charset_utf8mb3_bin) + m_cursor_name((const char*) NULL, 0, & my_charset_utf8mb3_bin), + m_row_number(0) { } void clear() @@ -331,6 +335,7 @@ protected: m_table_name.length(0); m_column_name.length(0); m_cursor_name.length(0); + m_row_number= 0; } }; @@ -434,16 +439,14 @@ private: @param level - the error level for this condition @param msg - the message text for this condition */ - Sql_condition(MEM_ROOT *mem_root, - const Sql_condition_identity &value, - const char *msg) - :Sql_condition_identity(value), - m_mem_root(mem_root) + Sql_condition(MEM_ROOT *mem_root, const Sql_condition_identity &value, + const char *msg, ulong current_row_for_warning) + : Sql_condition_identity(value), m_mem_root(mem_root) { - DBUG_ASSERT(mem_root != NULL); DBUG_ASSERT(value.get_sql_errno() != 0); DBUG_ASSERT(msg != NULL); set_builtin_message_text(msg); + m_row_number= current_row_for_warning; } /** Destructor. */ @@ -720,7 +723,7 @@ private: void inc_current_row_for_warning() { m_current_row_for_warning++; } /** Reset the current row counter. Start counting from the first row. */ - void reset_current_row_for_warning() { m_current_row_for_warning= 1; } + void reset_current_row_for_warning(int n) { m_current_row_for_warning= n; } /** Return the current counter value. */ ulong current_row_for_warning() const { return m_current_row_for_warning; } @@ -742,9 +745,8 @@ private: @return a pointer to the added SQL-condition. */ - Sql_condition *push_warning(THD *thd, - const Sql_condition_identity *identity, - const char* msg); + Sql_condition *push_warning(THD *thd, const Sql_condition_identity *identity, + const char* msg, ulong current_row_number); /** Add a new SQL-condition to the current list and increment the respective @@ -1143,8 +1145,8 @@ public: void inc_current_row_for_warning() { get_warning_info()->inc_current_row_for_warning(); } - void reset_current_row_for_warning() - { get_warning_info()->reset_current_row_for_warning(); } + void reset_current_row_for_warning(int n) + { get_warning_info()->reset_current_row_for_warning(n); } bool is_warning_info_read_only() const { return get_warning_info()->is_read_only(); } @@ -1175,10 +1177,12 @@ public: const char* sqlstate, Sql_condition::enum_warning_level level, const Sql_user_condition_identity &ucid, - const char* msg) + const char* msg, + ulong current_row_number) { Sql_condition_identity tmp(sql_errno_arg, sqlstate, level, ucid); - return get_warning_info()->push_warning(thd, &tmp, msg); + return get_warning_info()->push_warning(thd, &tmp, msg, + current_row_number); } Sql_condition *push_warning(THD *thd, @@ -1188,7 +1192,7 @@ public: const char* msg) { return push_warning(thd, sqlerrno, sqlstate, level, - Sql_user_condition_identity(), msg); + Sql_user_condition_identity(), msg, 0); } void mark_sql_conditions_for_removal() { get_warning_info()->mark_sql_conditions_for_removal(); } diff --git a/sql/sql_get_diagnostics.cc b/sql/sql_get_diagnostics.cc index 197bf5e7a00..240975d2974 100644 --- a/sql/sql_get_diagnostics.cc +++ b/sql/sql_get_diagnostics.cc @@ -338,6 +338,8 @@ Condition_information_item::get_value(THD *thd, const Sql_condition *cond) str.set_ascii(cond->get_sqlstate(), strlen(cond->get_sqlstate())); value= make_utf8_string_item(thd, &str); break; + case ROW_NUMBER: + value= new (thd->mem_root) Item_uint(thd, cond->m_row_number); } DBUG_RETURN(value); diff --git a/sql/sql_get_diagnostics.h b/sql/sql_get_diagnostics.h index f283aa5b2c6..efe526d7c61 100644 --- a/sql/sql_get_diagnostics.h +++ b/sql/sql_get_diagnostics.h @@ -254,7 +254,8 @@ public: CURSOR_NAME, MESSAGE_TEXT, MYSQL_ERRNO, - RETURNED_SQLSTATE + RETURNED_SQLSTATE, + ROW_NUMBER }; /** diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 4f57d6f98de..707b8a0d3bf 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -229,6 +229,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, } if (values.elements != table->s->visible_fields) { + thd->get_stmt_da()->reset_current_row_for_warning(1); my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L); DBUG_RETURN(-1); } @@ -253,6 +254,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, if (fields.elements != values.elements) { + thd->get_stmt_da()->reset_current_row_for_warning(1); my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L); DBUG_RETURN(-1); } @@ -699,7 +701,6 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list, const bool was_insert_delayed= (table_list->lock_type == TL_WRITE_DELAYED); bool using_bulk_insert= 0; uint value_count; - ulong counter = 1; /* counter of iteration in bulk PS operation*/ ulonglong iteration= 0; ulonglong id; @@ -830,10 +831,11 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list, while ((values= its++)) { - counter++; + thd->get_stmt_da()->inc_current_row_for_warning(); if (values->elements != value_count) { - my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), counter); + my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), + thd->get_stmt_da()->current_row_for_warning()); goto abort; } if (setup_fields(thd, Ref_ptr_array(), @@ -842,6 +844,7 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list, switch_to_nullable_trigger_fields(*values, table); } its.rewind (); + thd->get_stmt_da()->reset_current_row_for_warning(0); /* Restore the current context. */ ctx_state.restore_state(context, table_list); @@ -1008,6 +1011,7 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list, while ((values= its++)) { + thd->get_stmt_da()->inc_current_row_for_warning(); if (fields.elements || !value_count) { /* @@ -1124,7 +1128,6 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list, if (unlikely(error)) break; info.accepted_rows++; - thd->get_stmt_da()->inc_current_row_for_warning(); } its.rewind(); iteration++; @@ -1157,8 +1160,13 @@ values_loop_end: table->file->ha_release_auto_increment(); if (using_bulk_insert) { - if (unlikely(table->file->ha_end_bulk_insert()) && - !error) + /* + if my_error() wasn't called yet on some specific row, end_bulk_insert() + can still do it, but the error shouldn't be for any specific row number + */ + if (!error) + thd->get_stmt_da()->reset_current_row_for_warning(0); + if (unlikely(table->file->ha_end_bulk_insert()) && !error) { table->file->print_error(my_errno,MYF(0)); error=1; @@ -1413,7 +1421,7 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view) DBUG_ASSERT(view->table != 0 && view->field_translation != 0); - (void) my_bitmap_init(&used_fields, used_fields_buff, table->s->fields, 0); + (void) my_bitmap_init(&used_fields, used_fields_buff, table->s->fields); bitmap_clear_all(&used_fields); view->contain_auto_increment= 0; @@ -1683,6 +1691,8 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, ctx_state.restore_state(context, table_list); } + thd->get_stmt_da()->reset_current_row_for_warning(1); + if (res) DBUG_RETURN(res); @@ -2785,7 +2795,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) my_bitmap_init(©->has_value_set, (my_bitmap_map*) (bitmap + bitmaps_used*share->column_bitmap_size), - share->fields, FALSE); + share->fields); } copy->tmp_set.bitmap= 0; // To catch errors bzero((char*) bitmap, share->column_bitmap_size * bitmaps_used); @@ -5070,7 +5080,8 @@ bool select_create::send_eof() { WSREP_DEBUG("select_create commit failed, thd: %llu err: %s %s", thd->thread_id, - wsrep_thd_transaction_state_str(thd), wsrep_thd_query(thd)); + wsrep_thd_transaction_state_str(thd), + wsrep_thd_query(thd)); mysql_mutex_unlock(&thd->LOCK_thd_data); abort_result_set(); DBUG_RETURN(true); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 3ef45de2e8e..b5f8cf4a886 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1342,7 +1342,7 @@ void lex_unlock_plugins(LEX *lex) /* release used plugins */ if (lex->plugins.elements) /* No function call and no mutex if no plugins. */ { - plugin_unlock_list(0, (plugin_ref*)lex->plugins.buffer, + plugin_unlock_list(0, (plugin_ref*)lex->plugins.buffer, lex->plugins.elements); } reset_dynamic(&lex->plugins); @@ -6042,8 +6042,10 @@ int st_select_lex_unit::save_union_explain_part2(Explain_query *output) bool LEX::is_partition_management() const { return (sql_command == SQLCOM_ALTER_TABLE && - (alter_info.partition_flags == ALTER_PARTITION_ADD || - alter_info.partition_flags == ALTER_PARTITION_REORGANIZE)); + (alter_info.partition_flags & (ALTER_PARTITION_ADD | + ALTER_PARTITION_CONVERT_IN | + ALTER_PARTITION_CONVERT_OUT | + ALTER_PARTITION_REORGANIZE))); } @@ -11252,6 +11254,25 @@ bool LEX::stmt_alter_table_exchange_partition(Table_ident *table) } +bool LEX::stmt_alter_table(Table_ident *table) +{ + DBUG_ASSERT(sql_command == SQLCOM_ALTER_TABLE); + first_select_lex()->db= table->db; + if (first_select_lex()->db.str == NULL && + copy_db_to(&first_select_lex()->db)) + return true; + if (unlikely(check_table_name(table->table.str, table->table.length, + false)) || + (table->db.str && unlikely(check_db_name((LEX_STRING*) &table->db)))) + { + my_error(ER_WRONG_TABLE_NAME, MYF(0), table->table.str); + return true; + } + name= table->table; + return false; +} + + void LEX::stmt_purge_to(const LEX_CSTRING &to) { type= 0; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 1fe8b869c11..14cf90caa04 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -290,7 +290,6 @@ class sp_pcontext; class sp_variable; class sp_expr_lex; class sp_assignment_lex; -class st_alter_tablespace; class partition_info; class Event_parse_data; class set_var_base; @@ -3521,12 +3520,6 @@ public: /* - Reference to a struct that contains information in various commands - to add/create/drop/change table spaces. - */ - st_alter_tablespace *alter_tablespace_info; - - /* The set of those tables whose fields are referenced in all subqueries of the query. TODO: possibly this it is incorrect to have used tables in LEX because @@ -4668,6 +4661,7 @@ public: void stmt_deallocate_prepare(const Lex_ident_sys_st &ident); bool stmt_alter_table_exchange_partition(Table_ident *table); + bool stmt_alter_table(Table_ident *table); void stmt_purge_to(const LEX_CSTRING &to); bool stmt_purge_before(Item *item); diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 2f1ee0b11bd..fe574db528f 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -659,6 +659,7 @@ int mysql_load(THD *thd, const sql_exchange *ex, TABLE_LIST *table_list, table->copy_blobs=1; thd->abort_on_warning= !ignore && thd->is_strict_mode(); + thd->get_stmt_da()->reset_current_row_for_warning(1); bool create_lookup_handler= handle_duplicates != DUP_ERROR; if ((table_list->table->file->ha_table_flags() & HA_DUPLICATE_POS)) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 0886fc85151..18629f4bd22 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -54,7 +54,6 @@ // check_mqh, // reset_mqh #include "sql_rename.h" // mysql_rename_tables -#include "sql_tablespace.h" // mysql_alter_tablespace #include "hostname.h" // hostname_cache_refresh #include "sql_test.h" // mysql_print_status #include "sql_select.h" // handle_select, mysql_select, @@ -878,7 +877,6 @@ void init_update_queries(void) sql_command_flags[SQLCOM_ALTER_PROCEDURE]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_ALTER_FUNCTION]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_TRUNCATE]|= CF_DISALLOW_IN_RO_TRANS; - sql_command_flags[SQLCOM_ALTER_TABLESPACE]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_REPAIR]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_OPTIMIZE]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_GRANT]|= CF_DISALLOW_IN_RO_TRANS; @@ -4136,7 +4134,7 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) If new master was not added, we still need to free mi. */ if (master_info_added) - master_info_index->remove_master_info(mi); + master_info_index->remove_master_info(mi, 1); else delete mi; } @@ -5888,12 +5886,6 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) case SQLCOM_XA_RECOVER: res= mysql_xa_recover(thd); break; - case SQLCOM_ALTER_TABLESPACE: - if (check_global_access(thd, CREATE_TABLESPACE_ACL)) - break; - if (!(res= mysql_alter_tablespace(thd, lex->alter_tablespace_info))) - my_ok(thd); - break; case SQLCOM_INSTALL_PLUGIN: if (! (res= mysql_install_plugin(thd, &thd->lex->comment, &thd->lex->ident))) @@ -6125,7 +6117,12 @@ finish: thd->wsrep_consistency_check= NO_CONSISTENCY_CHECK; if (wsrep_thd_is_toi(thd) || wsrep_thd_is_in_rsu(thd)) + { + WSREP_DEBUG("mysql_execute_command for %s", wsrep_thd_query(thd)); + THD_STAGE_INFO(thd, stage_waiting_isolation); wsrep_to_isolation_end(thd); + } + /* Force release of transactional locks if not in active MST and wsrep is on. */ @@ -7889,7 +7886,8 @@ static bool wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, DBUG_ASSERT(!debug_sync_set_action(thd, STRING_WITH_LEN(act))); }); WSREP_DEBUG("wsrep retrying AC query: %lu %s", - thd->wsrep_retry_counter, wsrep_thd_query(thd)); + thd->wsrep_retry_counter, + wsrep_thd_query(thd)); wsrep_prepare_for_autocommit_retry(thd, rawbuf, length, parser_state); if (thd->lex->explain) delete_explain_query(thd->lex); diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 0547331e7cd..b75a318ab65 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -67,7 +67,6 @@ #include "opt_range.h" // store_key_image_to_rec #include "sql_alter.h" // Alter_table_ctx #include "sql_select.h" -#include "sql_tablespace.h" // check_tablespace_name #include "ddl_log.h" #include "tztime.h" // my_tz_OFFSET0 @@ -78,11 +77,6 @@ using std::min; #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" -#define ERROR_INJECT_CRASH(code) \ - DBUG_EVALUATE_IF(code, (DBUG_SUICIDE(), 0), 0) -#define ERROR_INJECT_ERROR(code) \ - DBUG_EVALUATE_IF(code, (my_error(ER_UNKNOWN_ERROR, MYF(0)), TRUE), 0) - /* Partition related functions declarations and some static constants; */ @@ -525,7 +519,7 @@ static bool create_full_part_field_array(THD *thd, TABLE *table, goto end; } if (unlikely(my_bitmap_init(&part_info->full_part_field_set, bitmap_buf, - table->s->fields, FALSE))) + table->s->fields))) { result= TRUE; goto end; @@ -1100,10 +1094,10 @@ static bool set_up_partition_bitmaps(THD *thd, partition_info *part_info) bitmap_bytes * 2)))) DBUG_RETURN(TRUE); - my_bitmap_init(&part_info->read_partitions, bitmap_buf, bitmap_bits, FALSE); + my_bitmap_init(&part_info->read_partitions, bitmap_buf, bitmap_bits); /* Use the second half of the allocated buffer for lock_partitions */ my_bitmap_init(&part_info->lock_partitions, bitmap_buf + (bitmap_bytes / 4), - bitmap_bits, FALSE); + bitmap_bits); part_info->bitmaps_are_initialized= TRUE; part_info->set_partition_bitmaps(NULL); DBUG_RETURN(FALSE); @@ -2215,8 +2209,6 @@ static int add_partition_options(String *str, partition_element *p_elem) { int err= 0; - if (p_elem->tablespace_name) - err+= add_keyword_string(str,"TABLESPACE", false, p_elem->tablespace_name); if (p_elem->nodegroup_id != UNDEF_NODEGROUP) err+= add_keyword_int(str,"NODEGROUP",(longlong)p_elem->nodegroup_id); if (p_elem->part_max_rows) @@ -4714,8 +4706,6 @@ bool compare_partition_options(HA_CREATE_INFO *table_create_info, Note that there are not yet any engine supporting tablespace together with partitioning. TODO: when there are, add compare. */ - if (part_elem->tablespace_name || table_create_info->tablespace) - option_diffs[errors++]= "TABLESPACE"; if (part_elem->part_max_rows != table_create_info->max_rows) option_diffs[errors++]= "MAX_ROWS"; if (part_elem->part_min_rows != table_create_info->min_rows) @@ -4876,10 +4866,12 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, if (alter_info->partition_flags & (ALTER_PARTITION_ADD | ALTER_PARTITION_DROP | + ALTER_PARTITION_CONVERT_OUT | ALTER_PARTITION_COALESCE | ALTER_PARTITION_REORGANIZE | ALTER_PARTITION_TABLE_REORG | - ALTER_PARTITION_REBUILD)) + ALTER_PARTITION_REBUILD | + ALTER_PARTITION_CONVERT_IN)) { /* You can't add column when we are doing alter related to partition @@ -5085,6 +5077,13 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, } if (alter_info->partition_flags & ALTER_PARTITION_ADD) { + if ((alter_info->partition_flags & ALTER_PARTITION_CONVERT_IN) && + !(tab_part_info->part_type == RANGE_PARTITION || + tab_part_info->part_type == LIST_PARTITION)) + { + my_error(ER_ONLY_ON_RANGE_LIST_PARTITION, MYF(0), "CONVERT TABLE TO"); + goto err; + } if (*fast_alter_table && thd->locked_tables_mode) { MEM_ROOT *old_root= thd->mem_root; @@ -5371,8 +5370,12 @@ that are reorganised. tab_part_info->is_auto_partitioned= FALSE; } } - else if (alter_info->partition_flags & ALTER_PARTITION_DROP) + else if ((alter_info->partition_flags & ALTER_PARTITION_DROP) | + (alter_info->partition_flags & ALTER_PARTITION_CONVERT_OUT)) { + const char * const cmd= + (alter_info->partition_flags & ALTER_PARTITION_CONVERT_OUT) ? + "CONVERT" : "DROP"; /* Drop a partition from a range partition and list partitioning is always safe and can be made more or less immediate. It is necessary @@ -5401,7 +5404,7 @@ that are reorganised. if (!(tab_part_info->part_type == RANGE_PARTITION || tab_part_info->part_type == LIST_PARTITION)) { - my_error(ER_ONLY_ON_RANGE_LIST_PARTITION, MYF(0), "DROP"); + my_error(ER_ONLY_ON_RANGE_LIST_PARTITION, MYF(0), cmd); goto err; } if (num_parts_dropped >= tab_part_info->num_parts) @@ -5443,7 +5446,7 @@ that are reorganised. } while (++part_count < tab_part_info->num_parts); if (num_parts_found != num_parts_dropped) { - my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "DROP"); + my_error(ER_PARTITION_DOES_NOT_EXIST, MYF(0)); goto err; } if (table->file->is_fk_defined_on_table_or_index(MAX_KEY)) @@ -5451,7 +5454,17 @@ that are reorganised. my_error(ER_ROW_IS_REFERENCED, MYF(0)); goto err; } + DBUG_ASSERT(!(alter_info->partition_flags & ALTER_PARTITION_CONVERT_OUT) || + num_parts_dropped == 1); + /* NOTE: num_parts is used in generate_partition_syntax() */ tab_part_info->num_parts-= num_parts_dropped; + if ((alter_info->partition_flags & ALTER_PARTITION_CONVERT_OUT) && + tab_part_info->is_sub_partitioned()) + { + // TODO technically this can be converted to a *partitioned* table + my_error(ER_PARTITION_CONVERT_SUBPARTITIONED, MYF(0)); + goto err; + } } else if (alter_info->partition_flags & ALTER_PARTITION_REBUILD) { @@ -5459,7 +5472,7 @@ that are reorganised. tab_part_info->default_engine_type); if (set_part_state(alter_info, tab_part_info, PART_CHANGED)) { - my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "REBUILD"); + my_error(ER_PARTITION_DOES_NOT_EXIST, MYF(0)); goto err; } if (!(*fast_alter_table)) @@ -5735,7 +5748,7 @@ the generated partition syntax in a correct manner. } while (++part_count < tab_part_info->num_parts); if (drop_count != num_parts_reorged) { - my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "REORGANIZE"); + my_error(ER_PARTITION_DOES_NOT_EXIST, MYF(0)); goto err; } tab_part_info->num_parts= check_total_partitions; @@ -5795,7 +5808,7 @@ the generated partition syntax in a correct manner. goto err; } } - } // ADD, DROP, COALESCE, REORGANIZE, TABLE_REORG, REBUILD + } // ADD, DROP, COALESCE, REORGANIZE, TABLE_REORG, REBUILD, CONVERT else { /* @@ -6117,8 +6130,6 @@ static bool mysql_drop_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) char path[FN_REFLEN+1]; partition_info *part_info= lpt->table->part_info; List_iterator<partition_element> part_it(part_info->partitions); - uint i= 0; - uint remove_count= 0; int error; DBUG_ENTER("mysql_drop_partitions"); @@ -6133,34 +6144,69 @@ static bool mysql_drop_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) lpt->table->file->print_error(error, MYF(0)); DBUG_RETURN(TRUE); } - do - { - partition_element *part_elem= part_it++; - if (part_elem->part_state == PART_IS_DROPPED) - { - part_it.remove(); - remove_count++; - } - } while (++i < part_info->num_parts); - part_info->num_parts-= remove_count; DBUG_RETURN(FALSE); } /* - Insert log entry into list + Convert partition to a table in an ALTER TABLE of partitions + SYNOPSIS - insert_part_info_log_entry_list() - log_entry + alter_partition_convert_out() + lpt Struct containing parameters + RETURN VALUES - NONE + TRUE Failure + FALSE Success + + DESCRIPTION + Rename partition table marked with PART_TO_BE_DROPPED into a separate table + under the name lpt->alter_ctx->(new_db, new_name). + + This is ddl-logged by write_log_convert_out_partition(). */ -static void insert_part_info_log_entry_list(partition_info *part_info, - DDL_LOG_MEMORY_ENTRY *log_entry) +static bool alter_partition_convert_out(ALTER_PARTITION_PARAM_TYPE *lpt) { - log_entry->next_active_log_entry= part_info->first_log_entry; - part_info->first_log_entry= log_entry; + partition_info *part_info= lpt->table->part_info; + THD *thd= lpt->thd; + int error; + handler *file= get_new_handler(NULL, thd->mem_root, part_info->default_engine_type); + + DBUG_ASSERT(lpt->thd->mdl_context.is_lock_owner(MDL_key::TABLE, + lpt->table->s->db.str, + lpt->table->s->table_name.str, + MDL_EXCLUSIVE)); + + char from_name[FN_REFLEN + 1], to_name[FN_REFLEN + 1]; + const char *path= lpt->table->s->path.str; + + build_table_filename(to_name, sizeof(to_name) - 1, lpt->alter_ctx->new_db.str, + lpt->alter_ctx->new_name.str, "", 0); + + for (const partition_element &e: part_info->partitions) + { + if (e.part_state != PART_TO_BE_DROPPED) + continue; + + if (unlikely((error= create_partition_name(from_name, sizeof(from_name), + path, e.partition_name, + NORMAL_PART_NAME, FALSE)))) + { + DBUG_ASSERT(thd->is_error()); + return true; + } + if (DBUG_IF("error_convert_partition_00") || + unlikely(error= file->ha_rename_table(from_name, to_name))) + { + my_error(ER_ERROR_ON_RENAME, MYF(0), from_name, to_name, my_errno); + lpt->table->file->print_error(error, MYF(0)); + return true; + } + break; + } + + return false; } @@ -6188,50 +6234,44 @@ static void release_part_info_log_entries(DDL_LOG_MEMORY_ENTRY *log_entry) /* - Log an delete/rename frm file + Log an rename frm file SYNOPSIS - write_log_replace_delete_frm() + write_log_replace_frm() lpt Struct for parameters next_entry Next reference to use in log record from_path Name to rename from to_path Name to rename to - replace_flag TRUE if replace, else delete RETURN VALUES TRUE Error FALSE Success DESCRIPTION - Support routine that writes a replace or delete of an frm file into the + Support routine that writes a replace of an frm file into the ddl log. It also inserts an entry that keeps track of used space into the partition info object */ -static bool write_log_replace_delete_frm(ALTER_PARTITION_PARAM_TYPE *lpt, - uint next_entry, - const char *from_path, - const char *to_path, - bool replace_flag) +bool write_log_replace_frm(ALTER_PARTITION_PARAM_TYPE *lpt, + uint next_entry, + const char *from_path, + const char *to_path) { DDL_LOG_ENTRY ddl_log_entry; DDL_LOG_MEMORY_ENTRY *log_entry; - DBUG_ENTER("write_log_replace_delete_frm"); + DBUG_ENTER("write_log_replace_frm"); bzero(&ddl_log_entry, sizeof(ddl_log_entry)); - if (replace_flag) - ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION; - else - ddl_log_entry.action_type= DDL_LOG_DELETE_ACTION; + ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION; ddl_log_entry.next_entry= next_entry; lex_string_set(&ddl_log_entry.handler_name, reg_ext); lex_string_set(&ddl_log_entry.name, to_path); + lex_string_set(&ddl_log_entry.from_name, from_path); - if (replace_flag) - lex_string_set(&ddl_log_entry.from_name, from_path); if (ddl_log_write_entry(&ddl_log_entry, &log_entry)) { - DBUG_RETURN(TRUE); + DBUG_RETURN(true); } - insert_part_info_log_entry_list(lpt->part_info, log_entry); - DBUG_RETURN(FALSE); + ddl_log_add_entry(lpt->part_info, log_entry); + DBUG_RETURN(false); } @@ -6310,7 +6350,7 @@ static bool write_log_changed_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, *next_entry= log_entry->entry_pos; sub_elem->log_entry= log_entry; - insert_part_info_log_entry_list(part_info, log_entry); + ddl_log_add_entry(part_info, log_entry); } while (++j < num_subparts); } else @@ -6337,7 +6377,7 @@ static bool write_log_changed_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, } *next_entry= log_entry->entry_pos; part_elem->log_entry= log_entry; - insert_part_info_log_entry_list(part_info, log_entry); + ddl_log_add_entry(part_info, log_entry); } } } while (++i < num_elements); @@ -6346,21 +6386,29 @@ static bool write_log_changed_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, /* - Log dropped partitions + Log dropped or converted partitions SYNOPSIS - write_log_dropped_partitions() + log_drop_or_convert_action() lpt Struct containing parameters RETURN VALUES TRUE Error FALSE Success */ -static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, - uint *next_entry, - const char *path, - bool temp_list) +enum log_action_enum +{ + ACT_DROP = 0, + ACT_CONVERT_IN, + ACT_CONVERT_OUT +}; + +static bool log_drop_or_convert_action(ALTER_PARTITION_PARAM_TYPE *lpt, + uint *next_entry, const char *path, + const char *from_name, bool temp_list, + const log_action_enum convert_action) { DDL_LOG_ENTRY ddl_log_entry; + DBUG_ASSERT(convert_action == ACT_DROP || (from_name != NULL)); partition_info *part_info= lpt->part_info; DDL_LOG_MEMORY_ENTRY *log_entry; char tmp_path[FN_REFLEN + 1]; @@ -6368,10 +6416,13 @@ static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, List_iterator<partition_element> temp_it(part_info->temp_partitions); uint num_temp_partitions= part_info->temp_partitions.elements; uint num_elements= part_info->partitions.elements; - DBUG_ENTER("write_log_dropped_partitions"); + DBUG_ENTER("log_drop_or_convert_action"); bzero(&ddl_log_entry, sizeof(ddl_log_entry)); - ddl_log_entry.action_type= DDL_LOG_DELETE_ACTION; + + ddl_log_entry.action_type= convert_action ? + DDL_LOG_RENAME_ACTION : + DDL_LOG_DELETE_ACTION; if (temp_list) num_elements= num_temp_partitions; while (num_elements--) @@ -6392,8 +6443,13 @@ static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, name_variant= TEMP_PART_NAME; else name_variant= NORMAL_PART_NAME; + DBUG_ASSERT(convert_action != ACT_CONVERT_IN || + part_elem->part_state == PART_TO_BE_ADDED); + DBUG_ASSERT(convert_action != ACT_CONVERT_OUT || + part_elem->part_state == PART_TO_BE_DROPPED); if (part_info->is_sub_partitioned()) { + DBUG_ASSERT(!convert_action); List_iterator<partition_element> sub_it(part_elem->subpartitions); uint num_subparts= part_info->num_subparts; uint j= 0; @@ -6415,7 +6471,7 @@ static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, } *next_entry= log_entry->entry_pos; sub_elem->log_entry= log_entry; - insert_part_info_log_entry_list(part_info, log_entry); + ddl_log_add_entry(part_info, log_entry); } while (++j < num_subparts); } else @@ -6427,14 +6483,25 @@ static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, part_elem->partition_name, name_variant, TRUE)) DBUG_RETURN(TRUE); - lex_string_set(&ddl_log_entry.name, tmp_path); + switch (convert_action) + { + case ACT_CONVERT_OUT: + ddl_log_entry.from_name= { from_name, strlen(from_name) }; + /* fall through */ + case ACT_DROP: + ddl_log_entry.name= { tmp_path, strlen(tmp_path) }; + break; + case ACT_CONVERT_IN: + ddl_log_entry.name= { from_name, strlen(from_name) }; + ddl_log_entry.from_name= { tmp_path, strlen(tmp_path) }; + } if (ddl_log_write_entry(&ddl_log_entry, &log_entry)) { DBUG_RETURN(TRUE); } *next_entry= log_entry->entry_pos; part_elem->log_entry= log_entry; - insert_part_info_log_entry_list(part_info, log_entry); + ddl_log_add_entry(part_info, log_entry); } } } @@ -6442,21 +6509,37 @@ static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, } -/* - Set execute log entry in ddl log for this partitioned table - SYNOPSIS - set_part_info_exec_log_entry() - part_info Partition info object - exec_log_entry Log entry - RETURN VALUES - NONE -*/ - -static void set_part_info_exec_log_entry(partition_info *part_info, - DDL_LOG_MEMORY_ENTRY *exec_log_entry) +inline +static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, + uint *next_entry, const char *path, + bool temp_list) { - part_info->exec_log_entry= exec_log_entry; - exec_log_entry->next_active_log_entry= NULL; + return log_drop_or_convert_action(lpt, next_entry, path, NULL, temp_list, + ACT_DROP); +} + +inline +static bool write_log_convert_partition(ALTER_PARTITION_PARAM_TYPE *lpt, + uint *next_entry, const char *path) +{ + char other_table[FN_REFLEN + 1]; + const ulong f= lpt->alter_info->partition_flags; + DBUG_ASSERT((f & ALTER_PARTITION_CONVERT_IN) || (f & ALTER_PARTITION_CONVERT_OUT)); + const log_action_enum convert_action= (f & ALTER_PARTITION_CONVERT_IN) + ? ACT_CONVERT_IN : ACT_CONVERT_OUT; + build_table_filename(other_table, sizeof(other_table) - 1, lpt->alter_ctx->new_db.str, + lpt->alter_ctx->new_name.str, "", 0); + DDL_LOG_MEMORY_ENTRY *main_entry= lpt->part_info->main_entry; + bool res= log_drop_or_convert_action(lpt, next_entry, path, other_table, + false, convert_action); + /* + NOTE: main_entry is "drop shadow frm", we have to keep it like this + because partitioning crash-safety disables it at install shadow FRM phase. + This is needed to avoid spurious drop action when the shadow frm is replaced + by the backup frm and there is nothing to drop. + */ + lpt->part_info->main_entry= main_entry; + return res; } @@ -6464,10 +6547,9 @@ static void set_part_info_exec_log_entry(partition_info *part_info, Write the log entry to ensure that the shadow frm file is removed at crash. SYNOPSIS - write_log_drop_shadow_frm() + write_log_drop_frm() lpt Struct containing parameters - install_frm Should we log action to install shadow frm or should - the action be to remove the shadow frm file. + RETURN VALUES TRUE Error FALSE Success @@ -6476,36 +6558,53 @@ static void set_part_info_exec_log_entry(partition_info *part_info, file and its corresponding handler file. */ -static bool write_log_drop_shadow_frm(ALTER_PARTITION_PARAM_TYPE *lpt) +static bool write_log_drop_frm(ALTER_PARTITION_PARAM_TYPE *lpt, + DDL_LOG_STATE *drop_chain) { - partition_info *part_info= lpt->part_info; - DDL_LOG_MEMORY_ENTRY *log_entry; - DDL_LOG_MEMORY_ENTRY *exec_log_entry= NULL; - char shadow_path[FN_REFLEN + 1]; - DBUG_ENTER("write_log_drop_shadow_frm"); + char path[FN_REFLEN + 1]; + DBUG_ENTER("write_log_drop_frm"); + const DDL_LOG_STATE *main_chain= lpt->part_info; + const bool drop_backup= (drop_chain != main_chain); - build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt); + build_table_shadow_filename(path, sizeof(path) - 1, lpt, drop_backup); mysql_mutex_lock(&LOCK_gdl); - if (write_log_replace_delete_frm(lpt, 0UL, NULL, - (const char*)shadow_path, FALSE)) + if (ddl_log_delete_frm(drop_chain, (const char*)path)) goto error; - log_entry= part_info->first_log_entry; - if (ddl_log_write_execute_entry(log_entry->entry_pos, - &exec_log_entry)) + + if (drop_backup && (lpt->alter_info->partition_flags & ALTER_PARTITION_CONVERT_IN)) + { + TABLE_LIST *table_from= lpt->table_list->next_local; + build_table_filename(path, sizeof(path) - 1, table_from->db.str, + table_from->table_name.str, "", 0); + + if (ddl_log_delete_frm(drop_chain, (const char*) path)) + goto error; + } + + if (ddl_log_write_execute_entry(drop_chain->list->entry_pos, + drop_backup ? + main_chain->execute_entry->entry_pos : 0, + &drop_chain->execute_entry)) goto error; mysql_mutex_unlock(&LOCK_gdl); - set_part_info_exec_log_entry(part_info, exec_log_entry); DBUG_RETURN(FALSE); error: - release_part_info_log_entries(part_info->first_log_entry); + release_part_info_log_entries(drop_chain->list); mysql_mutex_unlock(&LOCK_gdl); - part_info->first_log_entry= NULL; + drop_chain->list= NULL; my_error(ER_DDL_LOG_ERROR, MYF(0)); DBUG_RETURN(TRUE); } +static inline +bool write_log_drop_shadow_frm(ALTER_PARTITION_PARAM_TYPE *lpt) +{ + return write_log_drop_frm(lpt, lpt->part_info); +} + + /* Log renaming of shadow frm to real frm name and dropping of old frm SYNOPSIS @@ -6523,20 +6622,20 @@ static bool write_log_rename_frm(ALTER_PARTITION_PARAM_TYPE *lpt) { partition_info *part_info= lpt->part_info; DDL_LOG_MEMORY_ENTRY *log_entry; - DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry; + DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->execute_entry; char path[FN_REFLEN + 1]; char shadow_path[FN_REFLEN + 1]; - DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry; + DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->list; DBUG_ENTER("write_log_rename_frm"); - part_info->first_log_entry= NULL; + part_info->list= NULL; build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt); mysql_mutex_lock(&LOCK_gdl); - if (write_log_replace_delete_frm(lpt, 0UL, shadow_path, path, TRUE)) + if (write_log_replace_frm(lpt, 0UL, shadow_path, path)) goto error; - log_entry= part_info->first_log_entry; - part_info->frm_log_entry= log_entry; + log_entry= part_info->list; + part_info->main_entry= log_entry; if (ddl_log_write_execute_entry(log_entry->entry_pos, &exec_log_entry)) goto error; @@ -6545,10 +6644,10 @@ static bool write_log_rename_frm(ALTER_PARTITION_PARAM_TYPE *lpt) DBUG_RETURN(FALSE); error: - release_part_info_log_entries(part_info->first_log_entry); + release_part_info_log_entries(part_info->list); mysql_mutex_unlock(&LOCK_gdl); - part_info->first_log_entry= old_first_log_entry; - part_info->frm_log_entry= NULL; + part_info->list= old_first_log_entry; + part_info->main_entry= NULL; my_error(ER_DDL_LOG_ERROR, MYF(0)); DBUG_RETURN(TRUE); } @@ -6573,25 +6672,25 @@ static bool write_log_drop_partition(ALTER_PARTITION_PARAM_TYPE *lpt) { partition_info *part_info= lpt->part_info; DDL_LOG_MEMORY_ENTRY *log_entry; - DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry; + DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->execute_entry; char tmp_path[FN_REFLEN + 1]; char path[FN_REFLEN + 1]; uint next_entry= 0; - DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry; + DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->list; DBUG_ENTER("write_log_drop_partition"); - part_info->first_log_entry= NULL; + part_info->list= NULL; build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); build_table_shadow_filename(tmp_path, sizeof(tmp_path) - 1, lpt); mysql_mutex_lock(&LOCK_gdl); if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path, FALSE)) goto error; - if (write_log_replace_delete_frm(lpt, next_entry, (const char*)tmp_path, - (const char*)path, TRUE)) + if (write_log_replace_frm(lpt, next_entry, (const char*)tmp_path, + (const char*)path)) goto error; - log_entry= part_info->first_log_entry; - part_info->frm_log_entry= log_entry; + log_entry= part_info->list; + part_info->main_entry= log_entry; if (ddl_log_write_execute_entry(log_entry->entry_pos, &exec_log_entry)) goto error; @@ -6600,15 +6699,44 @@ static bool write_log_drop_partition(ALTER_PARTITION_PARAM_TYPE *lpt) DBUG_RETURN(FALSE); error: - release_part_info_log_entries(part_info->first_log_entry); + release_part_info_log_entries(part_info->list); mysql_mutex_unlock(&LOCK_gdl); - part_info->first_log_entry= old_first_log_entry; - part_info->frm_log_entry= NULL; + part_info->list= old_first_log_entry; + part_info->main_entry= NULL; my_error(ER_DDL_LOG_ERROR, MYF(0)); DBUG_RETURN(TRUE); } +static bool write_log_convert_partition(ALTER_PARTITION_PARAM_TYPE *lpt) +{ + partition_info *part_info= lpt->part_info; + char tmp_path[FN_REFLEN + 1]; + char path[FN_REFLEN + 1]; + uint next_entry= part_info->list ? part_info->list->entry_pos : 0; + + build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); + build_table_shadow_filename(tmp_path, sizeof(tmp_path) - 1, lpt); + + mysql_mutex_lock(&LOCK_gdl); + + if (write_log_convert_partition(lpt, &next_entry, (const char*)path)) + goto error; + DBUG_ASSERT(next_entry == part_info->list->entry_pos); + if (ddl_log_write_execute_entry(part_info->list->entry_pos, + &part_info->execute_entry)) + goto error; + mysql_mutex_unlock(&LOCK_gdl); + return false; + +error: + mysql_mutex_unlock(&LOCK_gdl); + part_info->main_entry= NULL; + my_error(ER_DDL_LOG_ERROR, MYF(0)); + return true; +} + + /* Write the log entries to ensure that the add partition command is not executed at all if a crash before it has completed @@ -6630,11 +6758,10 @@ static bool write_log_add_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt) { partition_info *part_info= lpt->part_info; DDL_LOG_MEMORY_ENTRY *log_entry; - DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry; char tmp_path[FN_REFLEN + 1]; char path[FN_REFLEN + 1]; uint next_entry= 0; - DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry; + DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->list; /* write_log_drop_shadow_frm(lpt) must have been run first */ DBUG_ASSERT(old_first_log_entry); DBUG_ENTER("write_log_add_change_partition"); @@ -6649,20 +6776,18 @@ static bool write_log_add_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt) if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path, FALSE)) goto error; - log_entry= part_info->first_log_entry; + log_entry= part_info->list; if (ddl_log_write_execute_entry(log_entry->entry_pos, - /* Reuse the old execute ddl_log_entry */ - &exec_log_entry)) + &part_info->execute_entry)) goto error; mysql_mutex_unlock(&LOCK_gdl); - set_part_info_exec_log_entry(part_info, exec_log_entry); DBUG_RETURN(FALSE); error: - release_part_info_log_entries(part_info->first_log_entry); + release_part_info_log_entries(part_info->list); mysql_mutex_unlock(&LOCK_gdl); - part_info->first_log_entry= old_first_log_entry; + part_info->list= old_first_log_entry; my_error(ER_DDL_LOG_ERROR, MYF(0)); DBUG_RETURN(TRUE); } @@ -6694,10 +6819,10 @@ static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt) { partition_info *part_info= lpt->part_info; DDL_LOG_MEMORY_ENTRY *log_entry; - DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry; + DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->execute_entry; char path[FN_REFLEN + 1]; char shadow_path[FN_REFLEN + 1]; - DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry; + DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->list; uint next_entry= 0; DBUG_ENTER("write_log_final_change_partition"); @@ -6705,7 +6830,7 @@ static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt) Do not link any previous log entry. Replace the revert operations with forced retry operations. */ - part_info->first_log_entry= NULL; + part_info->list= NULL; build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt); mysql_mutex_lock(&LOCK_gdl); @@ -6715,10 +6840,10 @@ static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt) lpt->alter_info->partition_flags & ALTER_PARTITION_REORGANIZE)) goto error; - if (write_log_replace_delete_frm(lpt, next_entry, shadow_path, path, TRUE)) + if (write_log_replace_frm(lpt, next_entry, shadow_path, path)) goto error; - log_entry= part_info->first_log_entry; - part_info->frm_log_entry= log_entry; + log_entry= part_info->list; + part_info->main_entry= log_entry; /* Overwrite the revert execute log entry with this retry execute entry */ if (ddl_log_write_execute_entry(log_entry->entry_pos, &exec_log_entry)) @@ -6728,10 +6853,10 @@ static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt) DBUG_RETURN(FALSE); error: - release_part_info_log_entries(part_info->first_log_entry); + release_part_info_log_entries(part_info->list); mysql_mutex_unlock(&LOCK_gdl); - part_info->first_log_entry= old_first_log_entry; - part_info->frm_log_entry= NULL; + part_info->list= old_first_log_entry; + part_info->main_entry= NULL; my_error(ER_DDL_LOG_ERROR, MYF(0)); DBUG_RETURN(TRUE); } @@ -6748,11 +6873,15 @@ error: FALSE Success */ +/* + TODO: Partitioning atomic DDL refactoring: this should be replaced with + ddl_log_complete(). +*/ static void write_log_completed(ALTER_PARTITION_PARAM_TYPE *lpt, bool dont_crash) { partition_info *part_info= lpt->part_info; - DDL_LOG_MEMORY_ENTRY *log_entry= part_info->exec_log_entry; + DDL_LOG_MEMORY_ENTRY *log_entry= part_info->execute_entry; DBUG_ENTER("write_log_completed"); DBUG_ASSERT(log_entry); @@ -6768,11 +6897,11 @@ static void write_log_completed(ALTER_PARTITION_PARAM_TYPE *lpt, */ ; } - release_part_info_log_entries(part_info->first_log_entry); - release_part_info_log_entries(part_info->exec_log_entry); + release_part_info_log_entries(part_info->list); + release_part_info_log_entries(part_info->execute_entry); mysql_mutex_unlock(&LOCK_gdl); - part_info->exec_log_entry= NULL; - part_info->first_log_entry= NULL; + part_info->execute_entry= NULL; + part_info->list= NULL; DBUG_VOID_RETURN; } @@ -6786,14 +6915,18 @@ static void write_log_completed(ALTER_PARTITION_PARAM_TYPE *lpt, NONE */ +/* + TODO: Partitioning atomic DDL refactoring: this should be replaced with + ddl_log_release_entries(). +*/ static void release_log_entries(partition_info *part_info) { mysql_mutex_lock(&LOCK_gdl); - release_part_info_log_entries(part_info->first_log_entry); - release_part_info_log_entries(part_info->exec_log_entry); + release_part_info_log_entries(part_info->list); + release_part_info_log_entries(part_info->execute_entry); mysql_mutex_unlock(&LOCK_gdl); - part_info->first_log_entry= NULL; - part_info->exec_log_entry= NULL; + part_info->list= NULL; + part_info->execute_entry= NULL; } @@ -6859,6 +6992,10 @@ static int alter_close_table(ALTER_PARTITION_PARAM_TYPE *lpt) @param close_table Table is still open, close it before reverting */ +/* + TODO: Partitioning atomic DDL refactoring: this should be replaced with + correct combination of ddl_log_revert() / ddl_log_complete() +*/ static void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, bool action_completed, bool drop_partition, @@ -6906,8 +7043,8 @@ static void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL); } - if (part_info->first_log_entry && - ddl_log_execute_entry(thd, part_info->first_log_entry->entry_pos)) + if (part_info->list && + ddl_log_execute_entry(thd, part_info->list->entry_pos)) { /* We couldn't recover from error, most likely manual interaction @@ -7069,6 +7206,64 @@ bool log_partition_alter_to_ddl_log(ALTER_PARTITION_PARAM_TYPE *lpt) } +extern bool alter_partition_convert_in(ALTER_PARTITION_PARAM_TYPE *lpt); + +/** + Check that definition of source table fits definition of partition being + added and every row stored in the table conforms partition's expression. + + @param lpt Structure containing parameters required for checking + @param[in,out] part_file_name_buf Buffer for storing a partition name + @param part_file_name_buf_sz Size of buffer for storing a partition name + @param part_file_name_len Length of partition prefix stored in the buffer + on invocation of function + + @return false on success, true on error +*/ + +static bool check_table_data(ALTER_PARTITION_PARAM_TYPE *lpt) +{ + /* + TODO: if destination is partitioned by range(X) and source is indexed by X + then just get min(X) and max(X) from index. + */ + THD *thd= lpt->thd; + TABLE *table_to= lpt->table_list->table; + TABLE *table_from= lpt->table_list->next_local->table; + + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, + table_to->s->db.str, + table_to->s->table_name.str, + MDL_EXCLUSIVE)); + + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, + table_from->s->db.str, + table_from->s->table_name.str, + MDL_EXCLUSIVE)); + + uint32 new_part_id; + partition_element *part_elem; + const char* partition_name= thd->lex->part_info->curr_part_elem->partition_name; + part_elem= table_to->part_info->get_part_elem(partition_name, + nullptr, 0, &new_part_id); + if (unlikely(!part_elem)) + return true; + + if (unlikely(new_part_id == NOT_A_PARTITION_ID)) + { + DBUG_ASSERT(table_to->part_info->is_sub_partitioned()); + my_error(ER_PARTITION_INSTEAD_OF_SUBPARTITION, MYF(0)); + return true; + } + + if (verify_data_with_partition(table_from, table_to, new_part_id)) + { + return true; + } + + return false; +} + /** Actually perform the change requested by ALTER TABLE of partitions @@ -7093,11 +7288,23 @@ bool log_partition_alter_to_ddl_log(ALTER_PARTITION_PARAM_TYPE *lpt) uint fast_alter_partition_table(THD *thd, TABLE *table, Alter_info *alter_info, + Alter_table_ctx *alter_ctx, HA_CREATE_INFO *create_info, - TABLE_LIST *table_list, - const LEX_CSTRING *db, - const LEX_CSTRING *table_name) + TABLE_LIST *table_list) { + /* + TODO: Partitioning atomic DDL refactoring. + + DDL log chain state is stored in partition_info: + + struct st_ddl_log_memory_entry *first_log_entry; + struct st_ddl_log_memory_entry *exec_log_entry; + struct st_ddl_log_memory_entry *frm_log_entry; + + Make it stored and used in DDL_LOG_STATE like it was done in MDEV-17567. + This requires mysql_write_frm() refactoring (see comment there). + */ + /* Set-up struct used to write frm files */ partition_info *part_info; ALTER_PARTITION_PARAM_TYPE lpt_obj; @@ -7115,13 +7322,14 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, lpt->table_list= table_list; lpt->part_info= part_info; lpt->alter_info= alter_info; + lpt->alter_ctx= alter_ctx; lpt->create_info= create_info; lpt->db_options= create_info->table_options_with_row_type(); lpt->table= table; lpt->key_info_buffer= 0; lpt->key_count= 0; - lpt->db= *db; - lpt->table_name= *table_name; + lpt->db= alter_ctx->db; + lpt->table_name= alter_ctx->table_name; lpt->org_tabledef_version= table->s->tabledef_version; lpt->copied= 0; lpt->deleted= 0; @@ -7241,38 +7449,29 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, to test if recovery is properly done. */ if (write_log_drop_shadow_frm(lpt) || - ERROR_INJECT_CRASH("crash_drop_partition_1") || - ERROR_INJECT_ERROR("fail_drop_partition_1") || + ERROR_INJECT("drop_partition_1") || mysql_write_frm(lpt, WFRM_WRITE_SHADOW) || - ERROR_INJECT_CRASH("crash_drop_partition_2") || - ERROR_INJECT_ERROR("fail_drop_partition_2") || + ERROR_INJECT("drop_partition_2") || wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) || - ERROR_INJECT_CRASH("crash_drop_partition_3") || - ERROR_INJECT_ERROR("fail_drop_partition_3") || + ERROR_INJECT("drop_partition_3") || write_log_drop_partition(lpt) || (action_completed= TRUE, FALSE) || - ERROR_INJECT_CRASH("crash_drop_partition_4") || - ERROR_INJECT_ERROR("fail_drop_partition_4") || + ERROR_INJECT("drop_partition_4") || alter_close_table(lpt) || - ERROR_INJECT_CRASH("crash_drop_partition_5") || - ERROR_INJECT_ERROR("fail_drop_partition_5") || - ERROR_INJECT_CRASH("crash_drop_partition_6") || - ERROR_INJECT_ERROR("fail_drop_partition_6") || + ERROR_INJECT("drop_partition_5") || + ERROR_INJECT("drop_partition_6") || (frm_install= TRUE, FALSE) || mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) || log_partition_alter_to_ddl_log(lpt) || (frm_install= FALSE, FALSE) || - ERROR_INJECT_CRASH("crash_drop_partition_7") || - ERROR_INJECT_ERROR("fail_drop_partition_7") || + ERROR_INJECT("drop_partition_7") || mysql_drop_partitions(lpt) || - ERROR_INJECT_CRASH("crash_drop_partition_8") || - ERROR_INJECT_ERROR("fail_drop_partition_8") || + ERROR_INJECT("drop_partition_8") || (write_log_completed(lpt, FALSE), FALSE) || ((!thd->lex->no_write_to_binlog) && (write_bin_log(thd, FALSE, thd->query(), thd->query_length()), FALSE)) || - ERROR_INJECT_CRASH("crash_drop_partition_9") || - ERROR_INJECT_ERROR("fail_drop_partition_9")) + ERROR_INJECT("drop_partition_9")) { handle_alter_part_error(lpt, action_completed, TRUE, frm_install); goto err; @@ -7280,10 +7479,102 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, if (alter_partition_lock_handling(lpt)) goto err; } + else if (alter_info->partition_flags & ALTER_PARTITION_CONVERT_OUT) + { + DDL_LOG_STATE chain_drop_backup; + bzero(&chain_drop_backup, sizeof(chain_drop_backup)); + + if (mysql_write_frm(lpt, WFRM_WRITE_CONVERTED_TO) || + ERROR_INJECT("convert_partition_1") || + write_log_drop_shadow_frm(lpt) || + ERROR_INJECT("convert_partition_2") || + mysql_write_frm(lpt, WFRM_WRITE_SHADOW) || + ERROR_INJECT("convert_partition_3") || + wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) || + ERROR_INJECT("convert_partition_4") || + write_log_convert_partition(lpt) || + ERROR_INJECT("convert_partition_5") || + alter_close_table(lpt) || + ERROR_INJECT("convert_partition_6") || + alter_partition_convert_out(lpt) || + ERROR_INJECT("convert_partition_7") || + write_log_drop_frm(lpt, &chain_drop_backup) || + mysql_write_frm(lpt, WFRM_INSTALL_SHADOW|WFRM_BACKUP_ORIGINAL) || + log_partition_alter_to_ddl_log(lpt) || + ERROR_INJECT("convert_partition_8") || + ((!thd->lex->no_write_to_binlog) && + ((thd->binlog_xid= thd->query_id), + ddl_log_update_xid(lpt->part_info, thd->binlog_xid), + write_bin_log(thd, false, thd->query(), thd->query_length()), + (thd->binlog_xid= 0))) || + ERROR_INJECT("convert_partition_9")) + { + ddl_log_complete(&chain_drop_backup); + (void) ddl_log_revert(thd, lpt->part_info); + handle_alter_part_error(lpt, true, true, false); + goto err; + } + ddl_log_complete(lpt->part_info); + ERROR_INJECT("convert_partition_10"); + (void) ddl_log_revert(thd, &chain_drop_backup); + if (alter_partition_lock_handling(lpt) || + ERROR_INJECT("convert_partition_11")) + goto err; + } + else if ((alter_info->partition_flags & ALTER_PARTITION_CONVERT_IN)) + { + DDL_LOG_STATE chain_drop_backup; + bzero(&chain_drop_backup, sizeof(chain_drop_backup)); + TABLE *table_from= table_list->next_local->table; + + if (wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) || + wait_while_table_is_used(thd, table_from, HA_EXTRA_PREPARE_FOR_RENAME) || + ERROR_INJECT("convert_partition_1") || + compare_table_with_partition(thd, table_from, table, NULL, 0) || + ERROR_INJECT("convert_partition_2") || + check_table_data(lpt)) + goto err; + + if (write_log_drop_shadow_frm(lpt) || + ERROR_INJECT("convert_partition_3") || + mysql_write_frm(lpt, WFRM_WRITE_SHADOW) || + ERROR_INJECT("convert_partition_4") || + alter_close_table(lpt) || + ERROR_INJECT("convert_partition_5") || + write_log_convert_partition(lpt) || + ERROR_INJECT("convert_partition_6") || + alter_partition_convert_in(lpt) || + ERROR_INJECT("convert_partition_7") || + (frm_install= true, false) || + write_log_drop_frm(lpt, &chain_drop_backup) || + mysql_write_frm(lpt, WFRM_INSTALL_SHADOW|WFRM_BACKUP_ORIGINAL) || + log_partition_alter_to_ddl_log(lpt) || + (frm_install= false, false) || + ERROR_INJECT("convert_partition_8") || + ((!thd->lex->no_write_to_binlog) && + ((thd->binlog_xid= thd->query_id), + ddl_log_update_xid(lpt->part_info, thd->binlog_xid), + write_bin_log(thd, false, thd->query(), thd->query_length()), + (thd->binlog_xid= 0))) || + ERROR_INJECT("convert_partition_9")) + { + ddl_log_complete(&chain_drop_backup); + (void) ddl_log_revert(thd, lpt->part_info); + handle_alter_part_error(lpt, true, true, false); + goto err; + } + ddl_log_complete(lpt->part_info); + ERROR_INJECT("convert_partition_10"); + (void) ddl_log_revert(thd, &chain_drop_backup); + if (alter_partition_lock_handling(lpt) || + ERROR_INJECT("convert_partition_11")) + goto err; + } else if ((alter_info->partition_flags & ALTER_PARTITION_ADD) && (part_info->part_type == RANGE_PARTITION || part_info->part_type == LIST_PARTITION)) { + DBUG_ASSERT(!(alter_info->partition_flags & ALTER_PARTITION_CONVERT_IN)); /* ADD RANGE/LIST PARTITIONS In this case there are no tuples removed and no tuples are added. @@ -7315,41 +7606,31 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, 12)Complete query */ if (write_log_drop_shadow_frm(lpt) || - ERROR_INJECT_CRASH("crash_add_partition_1") || - ERROR_INJECT_ERROR("fail_add_partition_1") || + ERROR_INJECT("add_partition_1") || mysql_write_frm(lpt, WFRM_WRITE_SHADOW) || - ERROR_INJECT_CRASH("crash_add_partition_2") || - ERROR_INJECT_ERROR("fail_add_partition_2") || + ERROR_INJECT("add_partition_2") || wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) || - ERROR_INJECT_CRASH("crash_add_partition_3") || - ERROR_INJECT_ERROR("fail_add_partition_3") || + ERROR_INJECT("add_partition_3") || write_log_add_change_partition(lpt) || - ERROR_INJECT_CRASH("crash_add_partition_4") || - ERROR_INJECT_ERROR("fail_add_partition_4") || + ERROR_INJECT("add_partition_4") || mysql_change_partitions(lpt) || - ERROR_INJECT_CRASH("crash_add_partition_5") || - ERROR_INJECT_ERROR("fail_add_partition_5") || + ERROR_INJECT("add_partition_5") || alter_close_table(lpt) || - ERROR_INJECT_CRASH("crash_add_partition_6") || - ERROR_INJECT_ERROR("fail_add_partition_6") || - ERROR_INJECT_CRASH("crash_add_partition_7") || - ERROR_INJECT_ERROR("fail_add_partition_7") || + ERROR_INJECT("add_partition_6") || + ERROR_INJECT("add_partition_7") || write_log_rename_frm(lpt) || (action_completed= TRUE, FALSE) || - ERROR_INJECT_CRASH("crash_add_partition_8") || - ERROR_INJECT_ERROR("fail_add_partition_8") || + ERROR_INJECT("add_partition_8") || (frm_install= TRUE, FALSE) || mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) || log_partition_alter_to_ddl_log(lpt) || (frm_install= FALSE, FALSE) || - ERROR_INJECT_CRASH("crash_add_partition_9") || - ERROR_INJECT_ERROR("fail_add_partition_9") || + ERROR_INJECT("add_partition_9") || (write_log_completed(lpt, FALSE), FALSE) || ((!thd->lex->no_write_to_binlog) && (write_bin_log(thd, FALSE, thd->query(), thd->query_length()), FALSE)) || - ERROR_INJECT_CRASH("crash_add_partition_10") || - ERROR_INJECT_ERROR("fail_add_partition_10")) + ERROR_INJECT("add_partition_10")) { handle_alter_part_error(lpt, action_completed, FALSE, frm_install); goto err; @@ -7414,47 +7695,35 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, 13) Complete query. */ if (write_log_drop_shadow_frm(lpt) || - ERROR_INJECT_CRASH("crash_change_partition_1") || - ERROR_INJECT_ERROR("fail_change_partition_1") || + ERROR_INJECT("change_partition_1") || mysql_write_frm(lpt, WFRM_WRITE_SHADOW) || - ERROR_INJECT_CRASH("crash_change_partition_2") || - ERROR_INJECT_ERROR("fail_change_partition_2") || + ERROR_INJECT("change_partition_2") || write_log_add_change_partition(lpt) || - ERROR_INJECT_CRASH("crash_change_partition_3") || - ERROR_INJECT_ERROR("fail_change_partition_3") || + ERROR_INJECT("change_partition_3") || mysql_change_partitions(lpt) || - ERROR_INJECT_CRASH("crash_change_partition_4") || - ERROR_INJECT_ERROR("fail_change_partition_4") || + ERROR_INJECT("change_partition_4") || wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) || - ERROR_INJECT_CRASH("crash_change_partition_5") || - ERROR_INJECT_ERROR("fail_change_partition_5") || + ERROR_INJECT("change_partition_5") || alter_close_table(lpt) || - ERROR_INJECT_CRASH("crash_change_partition_6") || - ERROR_INJECT_ERROR("fail_change_partition_6") || + ERROR_INJECT("change_partition_6") || write_log_final_change_partition(lpt) || (action_completed= TRUE, FALSE) || - ERROR_INJECT_CRASH("crash_change_partition_7") || - ERROR_INJECT_ERROR("fail_change_partition_7") || - ERROR_INJECT_CRASH("crash_change_partition_8") || - ERROR_INJECT_ERROR("fail_change_partition_8") || + ERROR_INJECT("change_partition_7") || + ERROR_INJECT("change_partition_8") || ((frm_install= TRUE), FALSE) || mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) || log_partition_alter_to_ddl_log(lpt) || (frm_install= FALSE, FALSE) || - ERROR_INJECT_CRASH("crash_change_partition_9") || - ERROR_INJECT_ERROR("fail_change_partition_9") || + ERROR_INJECT("change_partition_9") || mysql_drop_partitions(lpt) || - ERROR_INJECT_CRASH("crash_change_partition_10") || - ERROR_INJECT_ERROR("fail_change_partition_10") || + ERROR_INJECT("change_partition_10") || mysql_rename_partitions(lpt) || - ERROR_INJECT_CRASH("crash_change_partition_11") || - ERROR_INJECT_ERROR("fail_change_partition_11") || + ERROR_INJECT("change_partition_11") || (write_log_completed(lpt, FALSE), FALSE) || ((!thd->lex->no_write_to_binlog) && (write_bin_log(thd, FALSE, thd->query(), thd->query_length()), FALSE)) || - ERROR_INJECT_CRASH("crash_change_partition_12") || - ERROR_INJECT_ERROR("fail_change_partition_12")) + ERROR_INJECT("change_partition_12")) { handle_alter_part_error(lpt, action_completed, FALSE, frm_install); goto err; diff --git a/sql/sql_partition.h b/sql/sql_partition.h index 57e6d0600ed..a90eaae0bae 100644 --- a/sql/sql_partition.h +++ b/sql/sql_partition.h @@ -55,6 +55,7 @@ typedef struct st_lock_param_type THD *thd; HA_CREATE_INFO *create_info; Alter_info *alter_info; + Alter_table_ctx *alter_ctx; TABLE *table; KEY *key_info_buffer; LEX_CSTRING db; @@ -64,6 +65,7 @@ typedef struct st_lock_param_type uint key_count; uint db_options; size_t pack_frm_len; + // TODO: remove duplicate data: part_info can be accessed via table->part_info partition_info *part_info; } ALTER_PARTITION_PARAM_TYPE; @@ -255,10 +257,9 @@ typedef int (*get_partitions_in_range_iter)(partition_info *part_info, #ifdef WITH_PARTITION_STORAGE_ENGINE uint fast_alter_partition_table(THD *thd, TABLE *table, Alter_info *alter_info, + Alter_table_ctx *alter_ctx, HA_CREATE_INFO *create_info, - TABLE_LIST *table_list, - const LEX_CSTRING *db, - const LEX_CSTRING *table_name); + TABLE_LIST *table_list); bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info, enum partition_state part_state); uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, @@ -278,7 +279,16 @@ bool verify_data_with_partition(TABLE *table, TABLE *part_table, uint32 part_id); bool compare_partition_options(HA_CREATE_INFO *table_create_info, partition_element *part_elem); +bool compare_table_with_partition(THD *thd, TABLE *table, + TABLE *part_table, + partition_element *part_elem, + uint part_id); bool partition_key_modified(TABLE *table, const MY_BITMAP *fields); +bool write_log_replace_frm(ALTER_PARTITION_PARAM_TYPE *lpt, + uint next_entry, + const char *from_path, + const char *to_path); + #else #define partition_key_modified(X,Y) 0 #endif diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc index 1056998ef08..fb1ae0d5fc7 100644 --- a/sql/sql_partition_admin.cc +++ b/sql/sql_partition_admin.cc @@ -192,10 +192,8 @@ static bool check_exchange_partition(TABLE *table, TABLE *part_table) @param part_table Partitioned table. @param part_elem Partition element to use for partition specific compare. */ -static bool compare_table_with_partition(THD *thd, TABLE *table, - TABLE *part_table, - partition_element *part_elem, - uint part_id) +bool compare_table_with_partition(THD *thd, TABLE *table, TABLE *part_table, + partition_element *part_elem, uint part_id) { HA_CREATE_INFO table_create_info, part_create_info; Alter_info part_alter_info; @@ -292,7 +290,7 @@ static bool compare_table_with_partition(THD *thd, TABLE *table, The workaround is to use REORGANIZE PARTITION to rewrite the frm file and then use EXCHANGE PARTITION when they are the same. */ - if (compare_partition_options(&table_create_info, part_elem)) + if (part_elem && compare_partition_options(&table_create_info, part_elem)) DBUG_RETURN(TRUE); DBUG_RETURN(FALSE); @@ -988,4 +986,53 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd) DBUG_RETURN(error); } + +/** + Move a table specified in the CONVERT TABLE <table_name> TO PARTITION ... + to the new partition. + + @param lpt A structure containing parameters regarding to the statement + ALTER TABLE ... TO PARTITION ... + @param part_file_name a file name of the partition being added + + @return false on success, true on error +*/ + +bool alter_partition_convert_in(ALTER_PARTITION_PARAM_TYPE *lpt) +{ + char part_file_name[2*FN_REFLEN+1]; + THD *thd= lpt->thd; + const char *path= lpt->table_list->table->s->path.str; + TABLE_LIST *table_from= lpt->table_list->next_local; + + const char *partition_name= + thd->lex->part_info->curr_part_elem->partition_name; + + if (create_partition_name(part_file_name, sizeof(part_file_name), path, + partition_name, NORMAL_PART_NAME, false)) + return true; + + char from_file_name[FN_REFLEN+1]; + + build_table_filename(from_file_name, sizeof(from_file_name), + table_from->db.str, table_from->table_name.str, "", 0); + + handler *file= get_new_handler(nullptr, thd->mem_root, + table_from->table->file->ht); + if (unlikely(!file)) + return true; + + close_all_tables_for_name(thd, table_from->table->s, + HA_EXTRA_PREPARE_FOR_RENAME, nullptr); + + bool res= file->ha_rename_table(from_file_name, part_file_name); + + if (res) + my_error(ER_ERROR_ON_RENAME, MYF(0), from_file_name, + part_file_name, my_errno); + + delete file; + return res; +} + #endif /* WITH_PARTITION_STORAGE_ENGINE */ diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index ab13e8cc569..e3ddd14fe6a 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -456,7 +456,7 @@ static int item_val_real(struct st_mysql_value *value, double *buf) static struct st_plugin_dl *plugin_dl_find(const LEX_CSTRING *dl) { - uint i; + size_t i; struct st_plugin_dl *tmp; DBUG_ENTER("plugin_dl_find"); for (i= 0; i < plugin_dl_array.elements; i++) @@ -473,7 +473,7 @@ static struct st_plugin_dl *plugin_dl_find(const LEX_CSTRING *dl) static st_plugin_dl *plugin_dl_insert_or_reuse(struct st_plugin_dl *plugin_dl) { - uint i; + size_t i; struct st_plugin_dl *tmp; DBUG_ENTER("plugin_dl_insert_or_reuse"); for (i= 0; i < plugin_dl_array.elements; i++) @@ -1068,6 +1068,8 @@ plugin_ref plugin_lock_by_name(THD *thd, const LEX_CSTRING *name, int type) plugin_ref rc= NULL; st_plugin_int *plugin; DBUG_ENTER("plugin_lock_by_name"); + if (!name->length) + DBUG_RETURN(NULL); mysql_mutex_lock(&LOCK_plugin); if ((plugin= plugin_find_internal(name, type))) rc= intern_plugin_lock(lex, plugin_int_to_ref(plugin)); @@ -1078,7 +1080,7 @@ plugin_ref plugin_lock_by_name(THD *thd, const LEX_CSTRING *name, int type) static st_plugin_int *plugin_insert_or_reuse(struct st_plugin_int *plugin) { - uint i; + size_t i; struct st_plugin_int *tmp; DBUG_ENTER("plugin_insert_or_reuse"); for (i= 0; i < plugin_array.elements; i++) @@ -1261,24 +1263,18 @@ static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check) remove_status_vars(show_vars); } - if (plugin_type_deinitialize[plugin->plugin->type]) - { - if ((*plugin_type_deinitialize[plugin->plugin->type])(plugin)) - { - sql_print_error("Plugin '%s' of type %s failed deinitialization", - plugin->name.str, plugin_type_names[plugin->plugin->type].str); - } - } - else if (plugin->plugin->deinit) + plugin_type_init deinit= plugin_type_deinitialize[plugin->plugin->type]; + if (!deinit) + deinit= (plugin_type_init)(plugin->plugin->deinit); + + if (deinit && deinit(plugin)) { - DBUG_PRINT("info", ("Deinitializing plugin: '%s'", plugin->name.str)); - if (plugin->plugin->deinit(plugin)) - { - DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.", - plugin->name.str)); - } + if (THD *thd= current_thd) + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, + WARN_PLUGIN_BUSY, ER_THD(thd, WARN_PLUGIN_BUSY)); } - plugin->state= PLUGIN_IS_UNINITIALIZED; + else + plugin->state= PLUGIN_IS_UNINITIALIZED; // free to unload if (ref_check && plugin->ref_count) sql_print_error("Plugin '%s' has ref_count=%d after deinitialization.", @@ -1286,10 +1282,13 @@ static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check) plugin_variables_deinit(plugin); } -static void plugin_del(struct st_plugin_int *plugin) +static void plugin_del(struct st_plugin_int *plugin, uint del_mask) { DBUG_ENTER("plugin_del"); mysql_mutex_assert_owner(&LOCK_plugin); + del_mask|= PLUGIN_IS_UNINITIALIZED | PLUGIN_IS_DISABLED; // always use these + if (!(plugin->state & del_mask)) + DBUG_VOID_RETURN; /* Free allocated strings before deleting the plugin. */ plugin_vars_free_values(plugin->system_vars); restore_ptr_backup(plugin->nbackups, plugin->ptr_backup); @@ -1307,7 +1306,7 @@ static void plugin_del(struct st_plugin_int *plugin) static void reap_plugins(void) { - uint count; + size_t count; struct st_plugin_int *plugin, **reap, **list; mysql_mutex_assert_owner(&LOCK_plugin); @@ -1339,19 +1338,19 @@ static void reap_plugins(void) list= reap; while ((plugin= *(--list))) - plugin_deinitialize(plugin, true); + plugin_deinitialize(plugin, true); mysql_mutex_lock(&LOCK_plugin); while ((plugin= *(--reap))) - plugin_del(plugin); + plugin_del(plugin, 0); my_afree(reap); } static void intern_plugin_unlock(LEX *lex, plugin_ref plugin) { - int i; + ssize_t i; st_plugin_int *pi; DBUG_ENTER("intern_plugin_unlock"); @@ -1417,7 +1416,7 @@ void plugin_unlock(THD *thd, plugin_ref plugin) } -void plugin_unlock_list(THD *thd, plugin_ref *list, uint count) +void plugin_unlock_list(THD *thd, plugin_ref *list, size_t count) { LEX *lex= thd ? thd->lex : 0; DBUG_ENTER("plugin_unlock_list"); @@ -1590,7 +1589,7 @@ static void init_plugin_psi_keys(void) {} */ int plugin_init(int *argc, char **argv, int flags) { - uint i; + size_t i; struct st_maria_plugin **builtins; struct st_maria_plugin *plugin; struct st_plugin_int tmp, *plugin_ptr, **reap; @@ -1782,7 +1781,7 @@ int plugin_init(int *argc, char **argv, int flags) reaped_mandatory_plugin= TRUE; plugin_deinitialize(plugin_ptr, true); mysql_mutex_lock(&LOCK_plugin); - plugin_del(plugin_ptr); + plugin_del(plugin_ptr, 0); } mysql_mutex_unlock(&LOCK_plugin); @@ -2021,7 +2020,7 @@ error: void plugin_shutdown(void) { - uint i, count= plugin_array.elements; + size_t i, count= plugin_array.elements; struct st_plugin_int **plugins, *plugin; struct st_plugin_dl **dl; DBUG_ENTER("plugin_shutdown"); @@ -2070,12 +2069,14 @@ void plugin_shutdown(void) plugins= (struct st_plugin_int **) my_alloca(sizeof(void*) * (count+1)); /* - If we have any plugins which did not die cleanly, we force shutdown + If we have any plugins which did not die cleanly, we force shutdown. + Don't re-deinit() plugins that failed deinit() earlier (already dying) */ for (i= 0; i < count; i++) { plugins[i]= *dynamic_element(&plugin_array, i, struct st_plugin_int **); - /* change the state to ensure no reaping races */ + if (plugins[i]->state == PLUGIN_IS_DYING) + plugins[i]->state= PLUGIN_IS_UNINITIALIZED; if (plugins[i]->state == PLUGIN_IS_DELETED) plugins[i]->state= PLUGIN_IS_DYING; } @@ -2111,9 +2112,7 @@ void plugin_shutdown(void) if (plugins[i]->ref_count) sql_print_error("Plugin '%s' has ref_count=%d after shutdown.", plugins[i]->name.str, plugins[i]->ref_count); - if (plugins[i]->state & PLUGIN_IS_UNINITIALIZED || - plugins[i]->state & PLUGIN_IS_DISABLED) - plugin_del(plugins[i]); + plugin_del(plugins[i], PLUGIN_IS_DYING); } /* @@ -2352,7 +2351,7 @@ static bool do_uninstall(THD *thd, TABLE *table, const LEX_CSTRING *name) of the delete from the plugin table, so that it is not replicated in row based mode. */ - table->file->row_logging= 0; // No logging + table->file->row_logging= 0; // No logging error= table->file->ha_delete_row(table->record[0]); if (unlikely(error)) { @@ -2465,7 +2464,7 @@ wsrep_error_label: bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func, int type, uint state_mask, void *arg) { - uint idx, total= 0; + size_t idx, total= 0; struct st_plugin_int *plugin; plugin_ref *plugins; my_bool res= FALSE; @@ -3316,7 +3315,7 @@ static void cleanup_variables(struct system_variables *vars) void plugin_thdvar_cleanup(THD *thd) { - uint idx; + size_t idx; plugin_ref *list; DBUG_ENTER("plugin_thdvar_cleanup"); @@ -4300,7 +4299,7 @@ void add_plugin_options(DYNAMIC_ARRAY *options, MEM_ROOT *mem_root) if (!initialized) return; - for (uint idx= 0; idx < plugin_array.elements; idx++) + for (size_t idx= 0; idx < plugin_array.elements; idx++) { p= *dynamic_element(&plugin_array, idx, struct st_plugin_int **); @@ -4407,7 +4406,7 @@ int thd_setspecific(MYSQL_THD thd, MYSQL_THD_KEY_T key, void *value) DBUG_ASSERT(key != INVALID_THD_KEY); if (key == INVALID_THD_KEY || (!thd && !(thd= current_thd))) return EINVAL; - + memcpy(intern_sys_var_ptr(thd, key, true), &value, sizeof(void*)); return 0; } diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index eaf0b40f34a..d4df8c6468f 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -172,7 +172,7 @@ extern plugin_ref plugin_lock(THD *thd, plugin_ref ptr); extern plugin_ref plugin_lock_by_name(THD *thd, const LEX_CSTRING *name, int type); extern void plugin_unlock(THD *thd, plugin_ref plugin); -extern void plugin_unlock_list(THD *thd, plugin_ref *list, uint count); +extern void plugin_unlock_list(THD *thd, plugin_ref *list, size_t count); extern bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name, const LEX_CSTRING *dl); extern bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name, diff --git a/sql/sql_plugin_services.ic b/sql/sql_plugin_services.ic index c6f07158003..3dd41ffbed9 100644 --- a/sql/sql_plugin_services.ic +++ b/sql/sql_plugin_services.ic @@ -216,7 +216,7 @@ static struct my_print_error_service_st my_print_error_handler= my_printv_error }; -struct json_service_st json_handler= +static struct json_service_st json_handler= { json_type, json_get_array_item, @@ -231,6 +231,92 @@ static struct thd_mdl_service_st thd_mdl_handler= thd_mdl_context }; +struct sql_service_st sql_service_handler= +{ + mysql_init, + mysql_real_connect_local, + mysql_real_connect, + mysql_errno, + mysql_error, + mysql_real_query, + mysql_affected_rows, + mysql_num_rows, + mysql_store_result, + mysql_free_result, + mysql_fetch_row, + mysql_close, +}; + +#define DEFINE_warning_function(name, ret) { \ + static query_id_t last_query_id= -1; \ + THD *thd= current_thd; \ + if((thd ? thd->query_id : 0) != last_query_id) \ + { \ + my_error(ER_PROVIDER_NOT_LOADED, MYF(ME_ERROR_LOG|ME_WARNING), name); \ + last_query_id= thd ? thd->query_id : 0; \ + } \ + return ret; \ +} + +#include <providers/lzma.h> +static struct provider_service_lzma_st provider_handler_lzma= +{ + DEFINE_lzma_stream_buffer_decode([]) DEFINE_warning_function("LZMA compression", LZMA_PROG_ERROR), + DEFINE_lzma_easy_buffer_encode([]) DEFINE_warning_function("LZMA compression", LZMA_PROG_ERROR), + + false // .is_loaded +}; +struct provider_service_lzma_st *provider_service_lzma= &provider_handler_lzma; + +#include <providers/lzo/lzo1x.h> +static struct provider_service_lzo_st provider_handler_lzo= +{ + DEFINE_lzo1x_1_15_compress([]) DEFINE_warning_function("LZO compression", LZO_E_INTERNAL_ERROR), + DEFINE_lzo1x_decompress_safe([]) DEFINE_warning_function("LZO compression", LZO_E_INTERNAL_ERROR), + + false // .is_loaded +}; +struct provider_service_lzo_st *provider_service_lzo= &provider_handler_lzo; + +#include <providers/bzlib.h> +static struct provider_service_bzip2_st provider_handler_bzip2= +{ + DEFINE_BZ2_bzBuffToBuffCompress([]) DEFINE_warning_function("BZip2 compression", -1), + DEFINE_BZ2_bzBuffToBuffDecompress([]) DEFINE_warning_function("BZip2 compression", -1), + DEFINE_BZ2_bzCompress([]) DEFINE_warning_function("BZip2 compression", -1), + DEFINE_BZ2_bzCompressEnd([]) DEFINE_warning_function("BZip2 compression", -1), + DEFINE_BZ2_bzCompressInit([]) DEFINE_warning_function("BZip2 compression", -1), + DEFINE_BZ2_bzDecompress([]) DEFINE_warning_function("BZip2 compression", -1), + DEFINE_BZ2_bzDecompressEnd([]) DEFINE_warning_function("BZip2 compression", -1), + DEFINE_BZ2_bzDecompressInit([]) DEFINE_warning_function("BZip2 compression", -1), + + false // .is_loaded +}; +struct provider_service_bzip2_st *provider_service_bzip2= &provider_handler_bzip2; + +#include <providers/snappy-c.h> +static struct provider_service_snappy_st provider_handler_snappy= +{ + DEFINE_snappy_max_compressed_length([]) -> size_t DEFINE_warning_function("Snappy compression", 0), + DEFINE_snappy_compress([]) DEFINE_warning_function("Snappy compression", SNAPPY_INVALID_INPUT), + DEFINE_snappy_uncompressed_length([]) DEFINE_warning_function("Snappy compression", SNAPPY_INVALID_INPUT), + DEFINE_snappy_uncompress([]) DEFINE_warning_function("Snappy compression", SNAPPY_INVALID_INPUT), + + false // .is_loaded +}; +struct provider_service_snappy_st *provider_service_snappy= &provider_handler_snappy; + +#include <providers/lz4.h> +static struct provider_service_lz4_st provider_handler_lz4= +{ + DEFINE_LZ4_compressBound([]) DEFINE_warning_function("LZ4 compression", 0), + DEFINE_LZ4_compress_default([]) DEFINE_warning_function("LZ4 compression", 0), + DEFINE_LZ4_decompress_safe([]) DEFINE_warning_function("LZ4 compression", -1), + + false // .is_loaded +}; +struct provider_service_lz4_st *provider_service_lz4= &provider_handler_lz4; + static struct st_service_ref list_of_services[]= { { "base64_service", VERSION_base64, &base64_handler }, @@ -255,5 +341,11 @@ static struct st_service_ref list_of_services[]= { "thd_wait_service", VERSION_thd_wait, &thd_wait_handler }, { "wsrep_service", VERSION_wsrep, &wsrep_handler }, { "json_service", VERSION_json, &json_handler }, - { "thd_mdl_service", VERSION_thd_mdl, &thd_mdl_handler } + { "thd_mdl_service", VERSION_thd_mdl, &thd_mdl_handler }, + { "sql_service", VERSION_sql_service, &sql_service_handler }, + { "provider_service_bzip2", VERSION_provider_bzip2, &provider_handler_bzip2 }, + { "provider_service_lz4", VERSION_provider_lz4, &provider_handler_lz4 }, + { "provider_service_lzma", VERSION_provider_lzma, &provider_handler_lzma }, + { "provider_service_lzo", VERSION_provider_lzo, &provider_handler_lzo }, + { "provider_service_snappy", VERSION_provider_snappy, &provider_handler_snappy } }; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 67032142591..68dd670787c 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -133,6 +133,7 @@ static const uint PARAMETER_FLAG_UNSIGNED= 128U << 8; #include "wsrep_trans_observer.h" #endif /* WITH_WSREP */ #include "xa.h" // xa_recover_get_fields +#include "sql_audit.h" // mysql_audit_release /** A result class used to send cursor rows using the binary protocol. @@ -1314,7 +1315,6 @@ static bool mysql_test_insert_common(Prepared_statement *stmt, if ((values= its++)) { uint value_count; - ulong counter= 0; Item *unused_conds= 0; if (table_list->table) @@ -1340,16 +1340,18 @@ static bool mysql_test_insert_common(Prepared_statement *stmt, } while ((values= its++)) { - counter++; if (values->elements != value_count) { - my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), counter); + my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), + thd->get_stmt_da()->current_row_for_warning()); goto error; } if (setup_fields(thd, Ref_ptr_array(), *values, COLUMNS_READ, 0, NULL, 0)) goto error; + thd->get_stmt_da()->inc_current_row_for_warning(); } + thd->get_stmt_da()->reset_current_row_for_warning(1); } DBUG_RETURN(FALSE); @@ -4048,19 +4050,22 @@ Execute_sql_statement(LEX_STRING sql_text) executions without having to cleanup/reset THD in between. */ -bool -Execute_sql_statement::execute_server_code(THD *thd) +static bool execute_server_code(THD *thd, + const char *sql_text, size_t sql_len) { PSI_statement_locker *parent_locker; bool error; + query_id_t save_query_id= thd->query_id; + query_id_t next_id= next_query_id(); - if (alloc_query(thd, m_sql_text.str, m_sql_text.length)) + if (alloc_query(thd, sql_text, sql_len)) return TRUE; Parser_state parser_state; if (parser_state.init(thd, thd->query(), thd->query_length())) return TRUE; + thd->query_id= next_id; parser_state.m_lip.multi_statements= FALSE; lex_start(thd); @@ -4078,17 +4083,23 @@ Execute_sql_statement::execute_server_code(THD *thd) /* report error issued during command execution */ if (likely(error == 0) && thd->spcont == NULL) - general_log_write(thd, COM_STMT_EXECUTE, + general_log_write(thd, COM_QUERY, thd->query(), thd->query_length()); end: thd->lex->restore_set_statement_var(); + thd->query_id= save_query_id; delete_explain_query(thd->lex); lex_end(thd->lex); return error; } +bool Execute_sql_statement::execute_server_code(THD *thd) +{ + return ::execute_server_code(thd, m_sql_text.str, m_sql_text.length); +} + /*************************************************************************** Prepared_statement ****************************************************************************/ @@ -4849,7 +4860,9 @@ Prepared_statement::execute_server_runnable(Server_runnable *server_runnable) Statement stmt_backup; bool error; Query_arena *save_stmt_arena= thd->stmt_arena; + Reprepare_observer *save_reprepare_observer= thd->m_reprepare_observer; Item_change_list save_change_list; + thd->Item_change_list::move_elements_to(&save_change_list); state= STMT_CONVENTIONAL_EXECUTION; @@ -4859,12 +4872,15 @@ Prepared_statement::execute_server_runnable(Server_runnable *server_runnable) thd->set_n_backup_statement(this, &stmt_backup); thd->set_n_backup_active_arena(this, &stmt_backup); + thd->stmt_arena= this; + thd->m_reprepare_observer= 0; error= server_runnable->execute_server_code(thd); thd->cleanup_after_query(); + thd->m_reprepare_observer= save_reprepare_observer; thd->restore_active_arena(this, &stmt_backup); thd->restore_backup_statement(this, &stmt_backup); thd->stmt_arena= save_stmt_arena; @@ -5581,14 +5597,6 @@ Ed_connection::store_result_set() return ed_result_set; } -/* - MENT-56 - Protocol_local and service_sql for plugins to enable 'local' SQL query execution. -*/ - -#ifndef EMBEDDED_LIBRARY -// This part is mostly copied from libmysqld/lib_sql.cc -// TODO: get rid of code duplications #include <mysql.h> #include "../libmysqld/embedded_priv.h" @@ -5604,11 +5612,14 @@ public: char **next_field; MYSQL_FIELD *next_mysql_field; MEM_ROOT *alloc; + THD *new_thd; + Security_context empty_ctx; - Protocol_local(THD *thd_arg, ulong prealloc= 0) : + Protocol_local(THD *thd_arg, THD *new_thd_arg, ulong prealloc) : Protocol_text(thd_arg, prealloc), - cur_data(0), first_data(0), data_tail(&first_data), alloc(0) - {} + cur_data(0), first_data(0), data_tail(&first_data), alloc(0), + new_thd(new_thd_arg) + {} protected: bool net_store_data(const uchar *from, size_t length); @@ -5680,6 +5691,20 @@ MYSQL_DATA *Protocol_local::alloc_new_dataset() } +void Protocol_local::clear_data_list() +{ + while (first_data) + { + MYSQL_DATA *data= first_data; + first_data= data->embedded_info->next; + free_rows(data); + } + data_tail= &first_data; + free_rows(cur_data); + cur_data= 0; +} + + static char *dup_str_aux(MEM_ROOT *root, const char *from, uint length, CHARSET_INFO *fromcs, CHARSET_INFO *tocs) { @@ -5973,7 +5998,6 @@ bool Protocol_local::send_result_set_metadata(List<Item> *list, uint flags) { List_iterator_fast<Item> it(*list); Item *item; -// Protocol_local prot(thd); DBUG_ENTER("send_result_set_metadata"); // if (!thd->mysql) // bootstrap file handling @@ -5984,7 +6008,7 @@ bool Protocol_local::send_result_set_metadata(List<Item> *list, uint flags) for (uint pos= 0 ; (item= it++); pos++) { - if (/*prot.*/store_item_metadata(thd, item, pos)) + if (store_item_metadata(thd, item, pos)) goto err; } @@ -5998,6 +6022,7 @@ bool Protocol_local::send_result_set_metadata(List<Item> *list, uint flags) DBUG_RETURN(1); /* purecov: inspected */ } + static void list_fields_send_default(THD *thd, Protocol_local *p, Field *fld, uint pos) { @@ -6085,19 +6110,6 @@ bool Protocol_local::store_null() #include <sql_common.h> #include <errmsg.h> -struct local_results -{ - struct st_mysql_data *cur_data; - struct st_mysql_data *first_data; - struct st_mysql_data **data_tail; - void clear_data_list(); - struct st_mysql_data *alloc_new_dataset(); - char **next_field; - MYSQL_FIELD *next_mysql_field; - MEM_ROOT *alloc; -}; - - static void embedded_get_error(MYSQL *mysql, MYSQL_DATA *data) { NET *net= &mysql->net; @@ -6112,11 +6124,11 @@ static void embedded_get_error(MYSQL *mysql, MYSQL_DATA *data) static my_bool loc_read_query_result(MYSQL *mysql) { - local_results *thd= (local_results *) mysql->thd; + Protocol_local *p= (Protocol_local *) mysql->thd; - MYSQL_DATA *res= thd->first_data; - DBUG_ASSERT(!thd->cur_data); - thd->first_data= res->embedded_info->next; + MYSQL_DATA *res= p->first_data; + DBUG_ASSERT(!p->cur_data); + p->first_data= res->embedded_info->next; if (res->embedded_info->last_errno && !res->embedded_info->fields_list) { @@ -6144,7 +6156,7 @@ static my_bool loc_read_query_result(MYSQL *mysql) if (res->embedded_info->fields_list) { mysql->status=MYSQL_STATUS_GET_RESULT; - thd->cur_data= res; + p->cur_data= res; } else my_free(res); @@ -6153,174 +6165,257 @@ static my_bool loc_read_query_result(MYSQL *mysql) } -static MYSQL_METHODS local_methods= +static my_bool +loc_advanced_command(MYSQL *mysql, enum enum_server_command command, + const uchar *header, ulong header_length, + const uchar *arg, ulong arg_length, my_bool skip_check, + MYSQL_STMT *stmt) { - loc_read_query_result, /* read_query_result */ - NULL/*loc_advanced_command*/, /* advanced_command */ - NULL/*loc_read_rows*/, /* read_rows */ - NULL/*loc_use_result*/, /* use_result */ - NULL/*loc_fetch_lengths*/, /* fetch_lengths */ - NULL/*loc_flush_use_result*/, /* flush_use_result */ - NULL/*loc_read_change_user_result*/ /* read_change_user_result */ -}; + my_bool result= 1; + Protocol_local *p= (Protocol_local *) mysql->thd; + NET *net= &mysql->net; + if (p->thd && p->thd->killed != NOT_KILLED) + { + if (p->thd->killed < KILL_CONNECTION) + p->thd->killed= NOT_KILLED; + else + return 1; + } -extern "C" MYSQL *mysql_real_connect_local(MYSQL *mysql, - const char *host, const char *user, const char *passwd, const char *db) -{ - //char name_buff[USERNAME_LENGTH]; + p->clear_data_list(); + /* Check that we are calling the client functions in right order */ + if (mysql->status != MYSQL_STATUS_READY) + { + set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); + goto end; + } - DBUG_ENTER("mysql_real_connect_local"); + /* Clear result variables */ + p->thd->clear_error(1); + mysql->affected_rows= ~(my_ulonglong) 0; + mysql->field_count= 0; + net_clear_error(net); - /* Test whether we're already connected */ - if (mysql->server_version) + /* + We have to call free_old_query before we start to fill mysql->fields + for new query. In the case of embedded server we collect field data + during query execution (not during data retrieval as it is in remote + client). So we have to call free_old_query here + */ + free_old_query(mysql); + + if (header) { - set_mysql_error(mysql, CR_ALREADY_CONNECTED, unknown_sqlstate); - DBUG_RETURN(0); + arg= header; + arg_length= header_length; } - if (!host || !host[0]) - host= mysql->options.host; + if (p->new_thd) + { + THD *thd_orig= current_thd; + set_current_thd(p->thd); + p->thd->thread_stack= (char*) &result; + p->thd->set_time(); + result= execute_server_code(p->thd, (const char *)arg, arg_length); + p->thd->cleanup_after_query(); + mysql_audit_release(p->thd); + p->end_statement(); + set_current_thd(thd_orig); + } + else + { + Ed_connection con(p->thd); + Security_context *ctx_orig= p->thd->security_ctx; + MYSQL_LEX_STRING sql_text; + DBUG_ASSERT(current_thd == p->thd); + sql_text.str= (char *) arg; + sql_text.length= arg_length; + p->thd->security_ctx= &p->empty_ctx; + result= con.execute_direct(p, sql_text); + p->thd->security_ctx= ctx_orig; + } + if (skip_check) + result= 0; + p->cur_data= 0; - mysql->methods= &local_methods; +end: + return result; +} - if (!db || !db[0]) - db=mysql->options.db; - if (!user || !user[0]) - user=mysql->options.user; +/* + reads dataset from the next query result - mysql->user= my_strdup(PSI_INSTRUMENT_ME, user, MYF(0)); + SYNOPSIS + loc_read_rows() + mysql connection handle + other parameters are not used + NOTES + It just gets next MYSQL_DATA from the result's queue - mysql->info_buffer= (char *) my_malloc(PSI_INSTRUMENT_ME, - MYSQL_ERRMSG_SIZE, MYF(0)); - //mysql->thd= create_embedded_thd(client_flag); + RETURN + pointer to MYSQL_DATA with the coming recordset +*/ + +static MYSQL_DATA * +loc_read_rows(MYSQL *mysql, MYSQL_FIELD *mysql_fields __attribute__((unused)), + unsigned int fields __attribute__((unused))) +{ + MYSQL_DATA *result= ((Protocol_local *)mysql->thd)->cur_data; + ((Protocol_local *)mysql->thd)->cur_data= 0; + if (result->embedded_info->last_errno) + { + embedded_get_error(mysql, result); + return NULL; + } + *result->embedded_info->prev_ptr= NULL; + return result; +} - //init_embedded_mysql(mysql, client_flag); - //if (mysql_init_character_set(mysql)) - // goto error; +/************************************************************************** + Get column lengths of the current row + If one uses mysql_use_result, res->lengths contains the length information, + else the lengths are calculated from the offset between pointers. +**************************************************************************/ - //if (check_embedded_connection(mysql, db)) - // goto error; +static void loc_fetch_lengths(ulong *to, MYSQL_ROW column, + unsigned int field_count) +{ + MYSQL_ROW end; - mysql->server_status= SERVER_STATUS_AUTOCOMMIT; + for (end=column + field_count; column != end ; column++,to++) + *to= *column ? *(uint *)((*column) - sizeof(uint)) : 0; +} - //if (mysql->options.init_commands) - //{ - // DYNAMIC_ARRAY *init_commands= mysql->options.init_commands; - // char **ptr= (char**)init_commands->buffer; - // char **end= ptr + init_commands->elements; -// - // for (; ptr<end; ptr++) - // { - // MYSQL_RES *res; - // if (mysql_query(mysql,*ptr)) - // goto error; - // if (mysql->fields) - // { - // if (!(res= (*mysql->methods->use_result)(mysql))) - // goto error; - // mysql_free_result(res); - // } - // } - //} - DBUG_PRINT("exit",("Mysql handler: %p", mysql)); - DBUG_RETURN(mysql); +static void loc_flush_use_result(MYSQL *mysql, my_bool) +{ + Protocol_local *p= (Protocol_local *) mysql->thd; + if (p->cur_data) + { + free_rows(p->cur_data); + p->cur_data= 0; + } + else if (p->first_data) + { + MYSQL_DATA *data= p->first_data; + p->first_data= data->embedded_info->next; + free_rows(data); + } +} -//error: - DBUG_PRINT("error",("message: %u (%s)", - mysql->net.last_errno, - mysql->net.last_error)); + +static void loc_on_close_free(MYSQL *mysql) +{ + Protocol_local *p= (Protocol_local *) mysql->thd; + THD *thd= p->new_thd; + delete p; + if (thd) { - /* Free alloced memory */ - my_bool free_me=mysql->free_me; - free_old_query(mysql); - mysql->free_me=0; - mysql_close(mysql); - mysql->free_me=free_me; + delete thd; + local_connection_thread_count--; } - DBUG_RETURN(0); + my_free(mysql->info_buffer); + mysql->info_buffer= 0; } +static MYSQL_RES *loc_use_result(MYSQL *mysql) +{ + return mysql_store_result(mysql); +} -extern "C" int execute_sql_command(const char *command, - char *hosts, char *names, char *filters) +static MYSQL_METHODS local_methods= { - MYSQL_LEX_STRING sql_text; - THD *thd= current_thd; - THD *new_thd= 0; - int result; - my_bool qc_save= 0; - Reprepare_observer *save_reprepare_observer= nullptr; + loc_read_query_result, /* read_query_result */ + loc_advanced_command, /* advanced_command */ + loc_read_rows, /* read_rows */ + loc_use_result, /* use_result */ + loc_fetch_lengths, /* fetch_lengths */ + loc_flush_use_result, /* flush_use_result */ + NULL, /* read_change_user_result */ + loc_on_close_free /* on_close_free */ +#ifdef EMBEDDED_LIBRARY + ,NULL, /* list_fields */ + NULL, /* read_prepare_result */ + NULL, /* stmt_execute */ + NULL, /* read_binary_rows */ + NULL, /* unbuffered_fetch */ + NULL, /* read_statistics */ + NULL, /* next_result */ + NULL /* read_rows_from_cursor */ +#endif +}; + - if (!thd) +Atomic_counter<uint32_t> local_connection_thread_count; + +extern "C" MYSQL *mysql_real_connect_local(MYSQL *mysql) +{ + THD *thd_orig= current_thd; + THD *new_thd; + Protocol_local *p; + DBUG_ENTER("mysql_real_connect_local"); + + /* Test whether we're already connected */ + if (mysql->server_version) { + set_mysql_error(mysql, CR_ALREADY_CONNECTED, unknown_sqlstate); + DBUG_RETURN(0); + } + + mysql->methods= &local_methods; + mysql->user= NULL; + + mysql->info_buffer= (char *) my_malloc(PSI_INSTRUMENT_ME, + MYSQL_ERRMSG_SIZE, MYF(0)); + if (!thd_orig || thd_orig->lock) + { + /* + When we start with the empty current_thd (that happens when plugins + are loaded during the server start) or when some tables are locked + with the current_thd already (that happens when INSTALL PLUGIN + calls the plugin_init or with queries), we create the new THD for + the local connection. So queries with this MYSQL will be run with + it rather than the current THD. + */ + new_thd= new THD(0); - new_thd->thread_stack= (char*) &sql_text; + local_connection_thread_count++; + new_thd->thread_stack= (char*) &thd_orig; new_thd->store_globals(); new_thd->security_ctx->skip_grants(); new_thd->query_cache_is_applicable= 0; new_thd->variables.wsrep_on= 0; + /* + TOSO: decide if we should turn the auditing off + for such threads. + We can do it like this: + new_thd->audit_class_mask[0]= ~0; + */ bzero((char*) &new_thd->net, sizeof(new_thd->net)); - thd= new_thd; + set_current_thd(thd_orig); + thd_orig= new_thd; } else - { - if (thd->lock) - /* Doesn't work if the thread opened/locked tables already. */ - return 2; - - qc_save= thd->query_cache_is_applicable; - thd->query_cache_is_applicable= 0; - save_reprepare_observer= thd->m_reprepare_observer; - thd->m_reprepare_observer= nullptr; - } - sql_text.str= (char *) command; - sql_text.length= strlen(command); - { - Protocol_local p(thd); - Ed_connection con(thd); - result= con.execute_direct(&p, sql_text); - if (!result && p.first_data) - { - int nr= (int) p.first_data->rows; - MYSQL_ROWS *rows= p.first_data->data; - - while (nr--) - { - strcpy(hosts, rows->data[0]); - hosts+= strlen(hosts) + 1; - strcpy(names, rows->data[1]); - names+= strlen(names) + 1; - if (filters) - { - strcpy(filters, rows->data[2]); - filters+= strlen(filters) + 1; - } - rows= rows->next; - } - } - if (p.first_data) - { - if (p.alloc) - free_root(p.alloc, MYF(0)); - my_free(p.first_data); - } - } + new_thd= NULL; + p= new Protocol_local(thd_orig, new_thd, 0); if (new_thd) - delete new_thd; + new_thd->protocol= p; else { - thd->query_cache_is_applicable= qc_save; - thd->m_reprepare_observer= save_reprepare_observer; + p->empty_ctx.init(); + p->empty_ctx.skip_grants(); } - *hosts= 0; - return result; + mysql->thd= p; + mysql->server_status= SERVER_STATUS_AUTOCOMMIT; + + + DBUG_PRINT("exit",("Mysql handler: %p", mysql)); + DBUG_RETURN(mysql); } -#endif /*!EMBEDDED_LIBRARY*/ diff --git a/sql/sql_prepare.h b/sql/sql_prepare.h index 1e81b9f80e6..1a96df85a19 100644 --- a/sql/sql_prepare.h +++ b/sql/sql_prepare.h @@ -353,4 +353,6 @@ private: size_t m_column_count; /* TODO: change to point to metadata */ }; +extern Atomic_counter<uint32_t> local_connection_thread_count; + #endif // SQL_PREPARE_H diff --git a/sql/sql_priv.h b/sql/sql_priv.h index 2206f71c060..f7d8ef0da67 100644 --- a/sql/sql_priv.h +++ b/sql/sql_priv.h @@ -416,16 +416,6 @@ inline int hexchar_to_int(char c) /* This must match the path length limit in the ER_NOT_RW_DIR error msg. */ #define ER_NOT_RW_DIR_PATHSIZE 200 -#define IS_TABLESPACES_TABLESPACE_NAME 0 -#define IS_TABLESPACES_ENGINE 1 -#define IS_TABLESPACES_TABLESPACE_TYPE 2 -#define IS_TABLESPACES_LOGFILE_GROUP_NAME 3 -#define IS_TABLESPACES_EXTENT_SIZE 4 -#define IS_TABLESPACES_AUTOEXTEND_SIZE 5 -#define IS_TABLESPACES_MAXIMUM_SIZE 6 -#define IS_TABLESPACES_NODEGROUP_ID 7 -#define IS_TABLESPACES_TABLESPACE_COMMENT 8 - bool db_name_is_in_ignore_db_dirs_list(const char *dbase); #endif /* MYSQL_SERVER */ diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc index ddada6ad892..1dfa238de50 100644 --- a/sql/sql_reload.cc +++ b/sql/sql_reload.cc @@ -403,7 +403,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options, /* If not default connection and 'all' is used */ mi->release(); mysql_mutex_lock(&LOCK_active_mi); - if (master_info_index->remove_master_info(mi)) + if (master_info_index->remove_master_info(mi, 0)) result= 1; mysql_mutex_unlock(&LOCK_active_mi); } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 333e97d07ef..ae5cd82da4c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2561,7 +2561,7 @@ int JOIN::optimize_stage2() if (!conds && outer_join) { /* Handle the case where we have an OUTER JOIN without a WHERE */ - conds= (Item*) &Item_true; + conds= (Item*) Item_true; } if (impossible_where) @@ -2731,9 +2731,7 @@ int JOIN::optimize_stage2() if (conds && const_table_map != found_const_table_map && (select_options & SELECT_DESCRIBE)) - { - conds= (Item*) &Item_false; - } + conds= (Item*) Item_false; /* Cache constant expressions in WHERE, HAVING, ON clauses. */ cache_const_exprs(); @@ -3050,7 +3048,7 @@ int JOIN::optimize_stage2() having= having->remove_eq_conds(thd, &select_lex->having_value, true); if (select_lex->having_value == Item::COND_FALSE) { - having= (Item*) &Item_false; + having= (Item*) Item_false; zero_result_cause= "Impossible HAVING noticed after reading const tables"; error= 0; select_lex->mark_const_derived(zero_result_cause); @@ -4974,6 +4972,7 @@ mysql_select(THD *thd, TABLE_LIST *tables, List<Item> &fields, COND *conds, } } + thd->get_stmt_da()->reset_current_row_for_warning(1); /* Look for a table owned by an engine with the select_handler interface */ select_lex->pushdown_select= find_select_handler(thd, select_lex); @@ -5636,7 +5635,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, if (join->cond_value == Item::COND_FALSE) { join->impossible_where= true; - conds= (Item*) &Item_false; + conds= (Item*) Item_false; } join->cond_equal= NULL; @@ -7117,7 +7116,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, /* set a barrier for the array of SARGABLE_PARAM */ (*sargables)[0].field= 0; - if (my_init_dynamic_array2(thd->mem_root->m_psi_key, keyuse, sizeof(KEYUSE), + if (my_init_dynamic_array2(thd->mem_root->psi_key, keyuse, sizeof(KEYUSE), thd->alloc(sizeof(KEYUSE) * 20), 20, 64, MYF(MY_THREAD_SPECIFIC))) DBUG_RETURN(TRUE); @@ -11922,7 +11921,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) below to check if we should use 'quick' instead. */ DBUG_PRINT("info", ("Item_int")); - tmp= (Item*) &Item_true; + tmp= (Item*) Item_true; } } @@ -12466,9 +12465,9 @@ static bool generate_derived_keys(DYNAMIC_ARRAY *keyuse_array) { KEYUSE *keyuse= dynamic_element(keyuse_array, 0, KEYUSE*); - uint elements= keyuse_array->elements; + size_t elements= keyuse_array->elements; TABLE *prev_table= 0; - for (uint i= 0; i < elements; i++, keyuse++) + for (size_t i= 0; i < elements; i++, keyuse++) { if (!keyuse->table) break; @@ -15505,7 +15504,7 @@ COND *Item_cond_and::build_equal_items(THD *thd, if (!cond_args->elements && !cond_equal.current_level.elements && !eq_list.elements) - return (Item*) &Item_true; + return (Item*) Item_true; List_iterator_fast<Item_equal> it(cond_equal.current_level); while ((item_equal= it++)) @@ -15612,7 +15611,7 @@ COND *Item_func_eq::build_equal_items(THD *thd, Item_equal *item_equal; int n= cond_equal.current_level.elements + eq_list.elements; if (n == 0) - return (Item*) &Item_true; + return (Item*) Item_true; else if (n == 1) { if ((item_equal= cond_equal.current_level.pop())) @@ -16016,7 +16015,7 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels, List<Item> eq_list; Item_func_eq *eq_item= 0; if (((Item *) item_equal)->const_item() && !item_equal->val_int()) - return (Item*) &Item_false; + return (Item*) Item_false; Item *item_const= item_equal->get_const(); Item_equal_fields_iterator it(*item_equal); Item *head; @@ -16161,7 +16160,7 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels, switch (eq_list.elements) { case 0: - res= cond ? cond : (Item*) &Item_true; + res= cond ? cond : (Item*) Item_true; break; case 1: if (!cond || cond->is_bool_literal()) @@ -16414,9 +16413,9 @@ static void update_const_equal_items(THD *thd, COND *cond, JOIN_TAB *tab, Item *item; while ((item= li++)) update_const_equal_items(thd, item, tab, - (((Item_cond*) cond)->top_level() && - ((Item_cond*) cond)->functype() == - Item_func::COND_AND_FUNC)); + cond->is_top_level_item() && + ((Item_cond*) cond)->functype() == + Item_func::COND_AND_FUNC); } else if (cond->type() == Item::FUNC_ITEM && ((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC) @@ -17999,7 +17998,7 @@ Item_func_isnull::remove_eq_conds(THD *thd, Item::cond_result *cond_value, */ - Item *item0= (Item*) &Item_false; + Item *item0= (Item*) Item_false; Item *eq_cond= new(thd->mem_root) Item_func_eq(thd, args[0], item0); if (!eq_cond) return this; @@ -18566,20 +18565,19 @@ setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps, uint field_count) DBUG_ASSERT(table->s->virtual_fields == 0); - my_bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count, - FALSE); + my_bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count); bitmaps+= bitmap_size; my_bitmap_init(&table->tmp_set, - (my_bitmap_map*) bitmaps, field_count, FALSE); + (my_bitmap_map*) bitmaps, field_count); bitmaps+= bitmap_size; my_bitmap_init(&table->eq_join_set, - (my_bitmap_map*) bitmaps, field_count, FALSE); + (my_bitmap_map*) bitmaps, field_count); bitmaps+= bitmap_size; my_bitmap_init(&table->cond_set, - (my_bitmap_map*) bitmaps, field_count, FALSE); + (my_bitmap_map*) bitmaps, field_count); bitmaps+= bitmap_size; my_bitmap_init(&table->has_value_set, - (my_bitmap_map*) bitmaps, field_count, FALSE); + (my_bitmap_map*) bitmaps, field_count); /* write_set and all_set are copies of read_set */ table->def_write_set= table->def_read_set; table->s->all_set= table->def_read_set; @@ -18702,7 +18700,7 @@ TABLE *Create_tmp_table::start(THD *thd, (ulong) m_rows_limit, MY_TEST(m_group))); if (use_temp_pool && !(test_flags & TEST_KEEP_TMP_TABLES)) - m_temp_pool_slot = bitmap_lock_set_next(&temp_pool); + m_temp_pool_slot = temp_pool_set_next(); if (m_temp_pool_slot != MY_BIT_NONE) // we got a slot sprintf(path, "%s-%s-%lx-%i", tmp_file_prefix, param->tmp_name, @@ -19614,7 +19612,7 @@ void Create_tmp_table::cleanup_on_failure(THD *thd, TABLE *table) if (table) free_tmp_table(thd, table); if (m_temp_pool_slot != MY_BIT_NONE) - bitmap_lock_clear_bit(&temp_pool, m_temp_pool_slot); + temp_pool_clear_bit(m_temp_pool_slot); } @@ -20379,7 +20377,7 @@ free_tmp_table(THD *thd, TABLE *entry) (*ptr)->free(); if (entry->temp_pool_slot != MY_BIT_NONE) - bitmap_lock_clear_bit(&temp_pool, entry->temp_pool_slot); + temp_pool_clear_bit(entry->temp_pool_slot); plugin_unlock(0, entry->s->db_plugin); entry->alias.free(); @@ -21061,7 +21059,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) if (join_tab->on_precond && !join_tab->on_precond->val_int()) rc= NESTED_LOOP_NO_MORE_ROWS; } - join->thd->get_stmt_da()->reset_current_row_for_warning(); + join->thd->get_stmt_da()->reset_current_row_for_warning(1); if (rc != NESTED_LOOP_NO_MORE_ROWS && (rc= join_tab_execution_startup(join_tab)) < 0) @@ -28574,7 +28572,7 @@ JOIN::reoptimize(Item *added_where, table_map join_tables, { DYNAMIC_ARRAY added_keyuse; SARGABLE_PARAM *sargables= 0; /* Used only as a dummy parameter. */ - uint org_keyuse_elements; + size_t org_keyuse_elements; /* Re-run the REF optimizer to take into account the new conditions. */ if (update_ref_and_keys(thd, &added_keyuse, join_tab, table_count, added_where, @@ -28596,7 +28594,7 @@ JOIN::reoptimize(Item *added_where, table_map join_tables, reset_query_plan(); if (!keyuse.buffer && - my_init_dynamic_array(thd->mem_root->m_psi_key, &keyuse, sizeof(KEYUSE), + my_init_dynamic_array(thd->mem_root->psi_key, &keyuse, sizeof(KEYUSE), 20, 64, MYF(MY_THREAD_SPECIFIC))) { delete_dynamic(&added_keyuse); @@ -29739,7 +29737,7 @@ void JOIN::make_notnull_conds_for_range_scans() Found a IS NULL conjunctive predicate for a null-rejected field in the WHERE clause */ - conds= (Item*) &Item_false; + conds= (Item*) Item_false; cond_equal= 0; impossible_where= true; DBUG_VOID_RETURN; @@ -29762,7 +29760,7 @@ void JOIN::make_notnull_conds_for_range_scans() Found a IS NULL conjunctive predicate for a null-rejected field of the inner table of an outer join with ON expression tbl->on_expr */ - tbl->on_expr= (Item*) &Item_false; + tbl->on_expr= (Item*) Item_false; } } } @@ -29913,7 +29911,7 @@ void build_notnull_conds_for_inner_nest_of_outer_join(JOIN *join, if (used_tables && build_notnull_conds_for_range_scans(join, nest_tbl->on_expr, used_tables)) { - nest_tbl->on_expr= (Item*) &Item_false; + nest_tbl->on_expr= (Item*) Item_false; } li.rewind(); @@ -29927,7 +29925,7 @@ void build_notnull_conds_for_inner_nest_of_outer_join(JOIN *join, } else if (build_notnull_conds_for_range_scans(join, tbl->on_expr, tbl->table->map)) - tbl->on_expr= (Item*) &Item_false; + tbl->on_expr= (Item*) Item_false; } } } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index b706829451d..d01f84fe7d1 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -231,6 +231,9 @@ static my_bool show_plugins(THD *thd, plugin_ref plugin, case PLUGIN_IS_DISABLED: table->field[2]->store(STRING_WITH_LEN("DISABLED"), cs); break; + case PLUGIN_IS_DYING: + table->field[2]->store(STRING_WITH_LEN("INACTIVE"), cs); + break; case PLUGIN_IS_FREED: // filtered in fill_plugins, used in fill_all_plugins table->field[2]->store(STRING_WITH_LEN("NOT INSTALLED"), cs); break; @@ -324,7 +327,7 @@ int fill_plugins(THD *thd, TABLE_LIST *tables, COND *cond) TABLE *table= tables->table; if (plugin_foreach_with_mask(thd, show_plugins, MYSQL_ANY_PLUGIN, - ~(PLUGIN_IS_FREED | PLUGIN_IS_DYING), table)) + ~PLUGIN_IS_FREED, table)) DBUG_RETURN(1); DBUG_RETURN(0); @@ -354,7 +357,7 @@ int fill_all_plugins(THD *thd, TABLE_LIST *tables, COND *cond) plugin_dl_foreach(thd, 0, show_plugins, table); const char *wstr= lookup.db_value.str, *wend= wstr + lookup.db_value.length; - for (uint i=0; i < (uint) dirp->number_of_files; i++) + for (size_t i=0; i < dirp->number_of_files; i++) { FILEINFO *file= dirp->dir_entry+i; LEX_CSTRING dl= { file->name, strlen(file->name) }; @@ -952,7 +955,7 @@ find_files(THD *thd, Dynamic_array<LEX_CSTRING*> *files, LEX_CSTRING *db, if (!db) /* Return databases */ { - for (uint i=0; i < (uint) dirp->number_of_files; i++) + for (size_t i=0; i < dirp->number_of_files; i++) { FILEINFO *file= dirp->dir_entry+i; #ifdef USE_SYMDIR @@ -6717,7 +6720,7 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, for (uint i=0 ; i < show_table->s->keys ; i++,key_info++) { if ((key_info->flags & HA_INVISIBLE_KEY) && - DBUG_EVALUATE_IF("test_invisible_index", 0, 1)) + !DBUG_IF("test_invisible_index")) continue; KEY_PART_INFO *key_part= key_info->key_part; LEX_CSTRING *str; @@ -6725,7 +6728,7 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, for (uint j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++) { if (key_part->field->invisible >= INVISIBLE_SYSTEM && - DBUG_EVALUATE_IF("test_completely_invisible", 0, 1)) + !DBUG_IF("test_completely_invisible")) { /* NOTE: we will get SEQ_IN_INDEX gap inside the result if this key_part @@ -7385,13 +7388,7 @@ static void store_schema_partitions_record(THD *thd, TABLE *schema_table, table->field[23]->store(STRING_WITH_LEN("default"), cs); table->field[24]->set_notnull(); - if (part_elem->tablespace_name) - table->field[24]->store(part_elem->tablespace_name, - strlen(part_elem->tablespace_name), cs); - else - { - table->field[24]->set_null(); - } + table->field[24]->set_null(); // Tablespace } return; } @@ -8222,8 +8219,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) DBUG_RETURN(0); my_bitmap_map* bitmaps= (my_bitmap_map*) thd->alloc(bitmap_buffer_size(field_count)); - my_bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count, - FALSE); + my_bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count); table->read_set= &table->def_read_set; bitmap_clear_all(table->read_set); table_list->schema_table_param= tmp_table_param; @@ -9814,23 +9810,17 @@ int initialize_schema_table(st_plugin_int *plugin) int finalize_schema_table(st_plugin_int *plugin) { + int deinit_status= 0; ST_SCHEMA_TABLE *schema_table= (ST_SCHEMA_TABLE *)plugin->data; DBUG_ENTER("finalize_schema_table"); if (schema_table) { if (plugin->plugin->deinit) - { - DBUG_PRINT("info", ("Deinitializing plugin: '%s'", plugin->name.str)); - if (plugin->plugin->deinit(NULL)) - { - DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.", - plugin->name.str)); - } - } + deinit_status= plugin->plugin->deinit(NULL); my_free(schema_table); } - DBUG_RETURN(0); + DBUG_RETURN(deinit_status); } diff --git a/sql/sql_signal.cc b/sql/sql_signal.cc index 8e973f9b0b3..4e86cc4d782 100644 --- a/sql/sql_signal.cc +++ b/sql/sql_signal.cc @@ -44,6 +44,7 @@ const LEX_CSTRING Diag_condition_item_names[]= { STRING_WITH_LEN("CURSOR_NAME") }, { STRING_WITH_LEN("MESSAGE_TEXT") }, { STRING_WITH_LEN("MYSQL_ERRNO") }, + { STRING_WITH_LEN("ROW_NUMBER") }, { STRING_WITH_LEN("CONDITION_IDENTIFIER") }, { STRING_WITH_LEN("CONDITION_NUMBER") }, @@ -309,6 +310,26 @@ int Sql_cmd_common_signal::eval_signal_informations(THD *thd, Sql_condition *con cond->m_sql_errno= (int) code; } + set= m_set_signal_information.m_item[DIAG_ROW_NUMBER]; + if (set != NULL) + { + if (set->is_null()) + { + thd->raise_error_printf(ER_WRONG_VALUE_FOR_VAR, + "ROW_NUMBER", "NULL"); + goto end; + } + longlong row_number_value= set->val_int(); + if (row_number_value < 0) + { + str= set->val_str(& str_value); + thd->raise_error_printf(ER_WRONG_VALUE_FOR_VAR, + "ROW_NUMBER", str->c_ptr_safe()); + goto end; + } + cond->m_row_number= (ulong) row_number_value; + } + /* The various item->val_xxx() methods don't return an error code, but flag thd in case of failure. @@ -419,7 +440,8 @@ bool Sql_cmd_resignal::execute(THD *thd) DBUG_RETURN(result); } - Sql_condition signaled_err(thd->mem_root, *signaled, signaled->message); + Sql_condition signaled_err(thd->mem_root, *signaled, signaled->message, + signaled->m_row_number); if (m_cond) { diff --git a/sql/sql_string.h b/sql/sql_string.h index fe57c8153bb..795f80c3e08 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -484,6 +484,11 @@ public: if (str.Alloced_length) Alloced_length= (uint32) (str.Alloced_length - offset); } + LEX_CSTRING to_lex_cstring() const + { + LEX_CSTRING tmp= {Ptr, str_length}; + return tmp; + } inline LEX_CSTRING *get_value(LEX_CSTRING *res) { res->str= Ptr; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 9b3c3f5a26c..7d9fd0c516d 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -667,10 +667,12 @@ void build_lower_case_table_filename(char *buff, size_t bufflen, */ uint build_table_shadow_filename(char *buff, size_t bufflen, - ALTER_PARTITION_PARAM_TYPE *lpt) + ALTER_PARTITION_PARAM_TYPE *lpt, + bool backup) { char tmp_name[FN_REFLEN]; - my_snprintf(tmp_name, sizeof (tmp_name), "%s-shadow-%lx-%s", tmp_file_prefix, + my_snprintf(tmp_name, sizeof (tmp_name), "%s-%s-%lx-%s", tmp_file_prefix, + backup ? "backup" : "shadow", (ulong) current_thd->thread_id, lpt->table_name.str); return build_table_filename(buff, bufflen, lpt->db.str, tmp_name, "", FN_IS_TMP); @@ -704,6 +706,11 @@ uint build_table_shadow_filename(char *buff, size_t bufflen, tables since it only handles partitioned data if it exists. */ + +/* + TODO: Partitioning atomic DDL refactoring: WFRM_WRITE_SHADOW + should be merged with create_table_impl(frm_only == true). +*/ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) { /* @@ -717,8 +724,11 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) char shadow_frm_name[FN_REFLEN+1]; char frm_name[FN_REFLEN+1]; #ifdef WITH_PARTITION_STORAGE_ENGINE + char bak_path[FN_REFLEN+1]; + char bak_frm_name[FN_REFLEN+1]; char *part_syntax_buf; uint syntax_len; + partition_info *part_info= lpt->part_info; #endif DBUG_ENTER("mysql_write_frm"); @@ -777,6 +787,94 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) goto end; } } +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (flags & WFRM_WRITE_CONVERTED_TO) + { + THD *thd= lpt->thd; + Alter_table_ctx *alter_ctx= lpt->alter_ctx; + HA_CREATE_INFO *create_info= lpt->create_info; + + LEX_CSTRING new_path= { alter_ctx->get_new_path(), 0 }; + partition_info *work_part_info= thd->work_part_info; + handlerton *db_type= create_info->db_type; + DBUG_ASSERT(lpt->table->part_info); + DBUG_ASSERT(lpt->table->part_info == part_info); + handler *file= ((ha_partition *)(lpt->table->file))->get_child_handlers()[0]; + DBUG_ASSERT(file); + new_path.length= strlen(new_path.str); + strxnmov(frm_name, sizeof(frm_name) - 1, new_path.str, reg_ext, NullS); + create_info->alias= alter_ctx->table_name; + thd->work_part_info= NULL; + create_info->db_type= work_part_info->default_engine_type; + /* NOTE: partitioned temporary tables are not supported. */ + DBUG_ASSERT(!create_info->tmp_table()); + if (ddl_log_create_table(thd, part_info, create_info->db_type, &new_path, + &alter_ctx->new_db, &alter_ctx->new_name, true) || + ERROR_INJECT("create_before_create_frm")) + DBUG_RETURN(TRUE); + + if (mysql_prepare_create_table(thd, create_info, lpt->alter_info, + &lpt->db_options, file, + &lpt->key_info_buffer, &lpt->key_count, + C_ALTER_TABLE, alter_ctx->new_db, + alter_ctx->new_name)) + DBUG_RETURN(TRUE); + + lpt->create_info->table_options= lpt->db_options; + LEX_CUSTRING frm= build_frm_image(thd, alter_ctx->new_name, create_info, + lpt->alter_info->create_list, + lpt->key_count, lpt->key_info_buffer, + file); + if (unlikely(!frm.str)) + DBUG_RETURN(TRUE); + + thd->work_part_info= work_part_info; + create_info->db_type= db_type; + + ERROR_INJECT("alter_partition_after_create_frm"); + + error= writefile(frm_name, alter_ctx->new_db.str, alter_ctx->new_name.str, + create_info->tmp_table(), frm.str, frm.length); + my_free((void *) frm.str); + if (unlikely(error) || ERROR_INJECT("alter_partition_after_write_frm")) + { + mysql_file_delete(key_file_frm, frm_name, MYF(0)); + DBUG_RETURN(TRUE); + } + + DBUG_RETURN(false); + } + if (flags & WFRM_BACKUP_ORIGINAL) + { + build_table_filename(path, sizeof(path) - 1, lpt->db.str, + lpt->table_name.str, "", 0); + strxnmov(frm_name, sizeof(frm_name), path, reg_ext, NullS); + + build_table_shadow_filename(bak_path, sizeof(bak_path) - 1, lpt, true); + strxmov(bak_frm_name, bak_path, reg_ext, NullS); + + DDL_LOG_MEMORY_ENTRY *main_entry= part_info->main_entry; + mysql_mutex_lock(&LOCK_gdl); + if (write_log_replace_frm(lpt, part_info->list->entry_pos, + (const char*) bak_path, + (const char*) path) || + ddl_log_write_execute_entry(part_info->list->entry_pos, + &part_info->execute_entry)) + { + mysql_mutex_unlock(&LOCK_gdl); + DBUG_RETURN(TRUE); + } + mysql_mutex_unlock(&LOCK_gdl); + part_info->main_entry= main_entry; + if (mysql_file_rename(key_file_frm, frm_name, bak_frm_name, MYF(MY_WME))) + DBUG_RETURN(TRUE); + if (lpt->table->file->ha_create_partitioning_metadata(bak_path, path, + CHF_RENAME_FLAG)) + DBUG_RETURN(TRUE); + } +#else /* !WITH_PARTITION_STORAGE_ENGINE */ + DBUG_ASSERT(!(flags & WFRM_BACKUP_ORIGINAL)); +#endif /* !WITH_PARTITION_STORAGE_ENGINE */ if (flags & WFRM_INSTALL_SHADOW) { #ifdef WITH_PARTITION_STORAGE_ENGINE @@ -798,20 +896,25 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) completing this we write a new phase to the log entry that will deactivate it. */ - if (mysql_file_delete(key_file_frm, frm_name, MYF(MY_WME)) || + if (!(flags & WFRM_BACKUP_ORIGINAL) && ( + mysql_file_delete(key_file_frm, frm_name, MYF(MY_WME)) #ifdef WITH_PARTITION_STORAGE_ENGINE - lpt->table->file->ha_create_partitioning_metadata(path, shadow_path, + || lpt->table->file->ha_create_partitioning_metadata(path, shadow_path, CHF_DELETE_FLAG) || - ddl_log_increment_phase(part_info->frm_log_entry->entry_pos) || - (ddl_log_sync(), FALSE) || - mysql_file_rename(key_file_frm, - shadow_frm_name, frm_name, MYF(MY_WME)) || - lpt->table->file->ha_create_partitioning_metadata(path, shadow_path, - CHF_RENAME_FLAG)) -#else - mysql_file_rename(key_file_frm, - shadow_frm_name, frm_name, MYF(MY_WME))) + ddl_log_increment_phase(part_info->main_entry->entry_pos) || + (ddl_log_sync(), FALSE) #endif + )) + { + error= 1; + goto err; + } + if (mysql_file_rename(key_file_frm, shadow_frm_name, frm_name, MYF(MY_WME)) +#ifdef WITH_PARTITION_STORAGE_ENGINE + || lpt->table->file->ha_create_partitioning_metadata(path, shadow_path, + CHF_RENAME_FLAG) +#endif + ) { error= 1; goto err; @@ -850,8 +953,8 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) err: #ifdef WITH_PARTITION_STORAGE_ENGINE - ddl_log_increment_phase(part_info->frm_log_entry->entry_pos); - part_info->frm_log_entry= NULL; + ddl_log_increment_phase(part_info->main_entry->entry_pos); + part_info->main_entry= NULL; (void) ddl_log_sync(); #endif ; @@ -2758,8 +2861,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, */ if (sql_field->stored_in_db()) record_offset+= sql_field->pack_length; - if (sql_field->flags & VERS_SYSTEM_FIELD) - continue; } /* Update virtual fields' offset and give error if All fields are invisible */ @@ -3114,14 +3215,14 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (!sql_field || (sql_field->invisible > INVISIBLE_USER && !column->generated)) { - my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str); + my_error(ER_KEY_COLUMN_DOES_NOT_EXIST, MYF(0), column->field_name.str); DBUG_RETURN(TRUE); } if (sql_field->invisible > INVISIBLE_USER && !(sql_field->flags & VERS_SYSTEM_FIELD) && - !key->invisible && DBUG_EVALUATE_IF("test_invisible_index", 0, 1)) + !key->invisible && !DBUG_IF("test_invisible_index")) { - my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str); + my_error(ER_KEY_COLUMN_DOES_NOT_EXIST, MYF(0), column->field_name.str); DBUG_RETURN(TRUE); } while ((dup_column= cols2++) != column) @@ -4188,7 +4289,6 @@ err: @retval -1 table existed but IF NOT EXISTS was used */ -static int create_table_impl(THD *thd, DDL_LOG_STATE *ddl_log_state_create, DDL_LOG_STATE *ddl_log_state_rm, @@ -6042,9 +6142,8 @@ remove_key: if (!part_elem) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_DROP_PARTITION_NON_EXISTENT, - ER_THD(thd, ER_DROP_PARTITION_NON_EXISTENT), - "DROP"); + ER_PARTITION_DOES_NOT_EXIST, + ER_THD(thd, ER_PARTITION_DOES_NOT_EXIST)); names_it.remove(); } } @@ -8347,7 +8446,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, key_type= Key::UNIQUE; if (dropped_key_part) { - my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), dropped_key_part); + my_error(ER_KEY_COLUMN_DOES_NOT_EXIST, MYF(0), dropped_key_part); if (long_hash_key) { key_info->algorithm= HA_KEY_ALG_LONG_HASH; @@ -9624,7 +9723,8 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, Table maybe does not exist, but we got an exclusive lock on the name, now we can safely try to find out for sure. */ - if (ha_table_exists(thd, &alter_ctx.new_db, &alter_ctx.new_name)) + if (!(alter_info->partition_flags & ALTER_PARTITION_CONVERT_IN) && + ha_table_exists(thd, &alter_ctx.new_db, &alter_ctx.new_name)) { /* Table will be closed in do_command() */ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias.str); @@ -9932,10 +10032,8 @@ do_continue:; } // In-place execution of ALTER TABLE for partitioning. - DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info, - create_info, table_list, - &alter_ctx.db, - &alter_ctx.table_name)); + DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info, &alter_ctx, + create_info, table_list)); } #endif @@ -11047,7 +11145,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, if (ignore && !alter_ctx->fk_error_if_delete_row) to->file->extra(HA_EXTRA_IGNORE_DUP_KEY); - thd->get_stmt_da()->reset_current_row_for_warning(); + thd->get_stmt_da()->reset_current_row_for_warning(1); restore_record(to, s->default_values); // Create empty record to->reset_default_fields(); @@ -11511,16 +11609,11 @@ bool check_engine(THD *thd, const char *db_name, if (create_info->tmp_table() && ha_check_storage_engine_flag(*new_engine, HTON_TEMPORARY_NOT_SUPPORTED)) { - if (create_info->used_fields & HA_CREATE_USED_ENGINE) - { - my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), - hton_name(*new_engine)->str, "TEMPORARY"); - *new_engine= 0; - DBUG_RETURN(true); - } - *new_engine= myisam_hton; + my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), + hton_name(*new_engine)->str, "TEMPORARY"); + *new_engine= 0; + DBUG_RETURN(true); } - lex_string_set(&create_info->new_storage_engine_name, ha_resolve_storage_engine_name(*new_engine)); DBUG_RETURN(false); diff --git a/sql/sql_table.h b/sql/sql_table.h index aacb6c99f15..eaa03bfaf8c 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -20,6 +20,10 @@ #include <my_sys.h> // pthread_mutex_t #include "m_string.h" // LEX_CUSTRING +#define ERROR_INJECT(code) \ + ((DBUG_IF("crash_" code) && (DBUG_SUICIDE(), 0)) || \ + (DBUG_IF("fail_" code) && (my_error(ER_UNKNOWN_ERROR, MYF(0)), 1))) + class Alter_info; class Alter_table_ctx; class Column_definition; @@ -53,6 +57,8 @@ enum enum_explain_filename_mode #define WFRM_WRITE_SHADOW 1 #define WFRM_INSTALL_SHADOW 2 #define WFRM_KEEP_SHARE 4 +#define WFRM_WRITE_CONVERTED_TO 8 +#define WFRM_BACKUP_ORIGINAL 16 /* Flags for conversion functions. */ static const uint FN_FROM_IS_TMP= 1 << 0; @@ -77,7 +83,8 @@ bool check_mysql50_prefix(const char *name); uint build_table_filename(char *buff, size_t bufflen, const char *db, const char *table, const char *ext, uint flags); uint build_table_shadow_filename(char *buff, size_t bufflen, - ALTER_PARTITION_PARAM_TYPE *lpt); + ALTER_PARTITION_PARAM_TYPE *lpt, + bool backup= false); void build_lower_case_table_filename(char *buff, size_t bufflen, const LEX_CSTRING *db, const LEX_CSTRING *table, diff --git a/sql/sql_tablespace.cc b/sql/sql_tablespace.cc deleted file mode 100644 index bfbaf185243..00000000000 --- a/sql/sql_tablespace.cc +++ /dev/null @@ -1,72 +0,0 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ - -/* drop and alter of tablespaces */ - -#include "mariadb.h" -#include "sql_priv.h" -#include "unireg.h" -#include "sql_tablespace.h" -#include "sql_table.h" // write_bin_log -#include "sql_class.h" // THD - -int mysql_alter_tablespace(THD *thd, st_alter_tablespace *ts_info) -{ - int error= HA_ADMIN_NOT_IMPLEMENTED; - handlerton *hton= ts_info->storage_engine; - - DBUG_ENTER("mysql_alter_tablespace"); - /* - If the user haven't defined an engine, this will fallback to using the - default storage engine. - */ - if (hton == NULL) - { - hton= ha_default_handlerton(thd); - if (ts_info->storage_engine != 0) - push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - ER_WARN_USING_OTHER_HANDLER, - ER_THD(thd, ER_WARN_USING_OTHER_HANDLER), - hton_name(hton)->str, - ts_info->tablespace_name ? ts_info->tablespace_name - : ts_info->logfile_group_name); - } - - if (hton->alter_tablespace) - { - if (unlikely((error= hton->alter_tablespace(hton, thd, ts_info)))) - { - if (error == 1) - DBUG_RETURN(1); - - if (error == HA_ADMIN_NOT_IMPLEMENTED) - my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), ""); - else - my_error(error, MYF(0)); - - DBUG_RETURN(error); - } - } - else - { - push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - ER_ILLEGAL_HA_CREATE_OPTION, - ER_THD(thd, ER_ILLEGAL_HA_CREATE_OPTION), - hton_name(hton)->str, - "TABLESPACE or LOGFILE GROUP"); - } - error= write_bin_log(thd, FALSE, thd->query(), thd->query_length()); - DBUG_RETURN(error); -} diff --git a/sql/sql_tablespace.h b/sql/sql_tablespace.h deleted file mode 100644 index 0760935edfc..00000000000 --- a/sql/sql_tablespace.h +++ /dev/null @@ -1,24 +0,0 @@ -/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ - -#ifndef SQL_TABLESPACE_INCLUDED -#define SQL_TABLESPACE_INCLUDED - -class THD; -class st_alter_tablespace; - -int mysql_alter_tablespace(THD* thd, st_alter_tablespace *ts_info); - -#endif /* SQL_TABLESPACE_INCLUDED */ diff --git a/sql/sql_test.cc b/sql/sql_test.cc index e06600700bb..0d1a8313564 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -264,7 +264,7 @@ static void print_keyuse(KEYUSE *keyuse) void print_keyuse_array(DYNAMIC_ARRAY *keyuse_array) { DBUG_LOCK_FILE; - fprintf(DBUG_FILE, "KEYUSE array (%d elements)\n", keyuse_array->elements); + fprintf(DBUG_FILE, "KEYUSE array (%zu elements)\n", keyuse_array->elements); for(uint i=0; i < keyuse_array->elements; i++) print_keyuse((KEYUSE*)dynamic_array_ptr(keyuse_array, i)); DBUG_UNLOCK_FILE; diff --git a/sql/sql_type_fixedbin.h b/sql/sql_type_fixedbin.h new file mode 100644 index 00000000000..332b712db31 --- /dev/null +++ b/sql/sql_type_fixedbin.h @@ -0,0 +1,1912 @@ +#ifndef SQL_TYPE_FIXEDBIN_H +#define SQL_TYPE_FIXEDBIN_H +/* Copyright (c) 2019,2021 MariaDB Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +/* + This is a common code for plugin (?) types that are generally + handled like strings, but have their own fixed size on-disk binary storage + format and their own (variable size) canonical string representation. + + Examples are INET6 and UUID types. +*/ + +#define MYSQL_SERVER +#include "sql_class.h" // THD, SORT_FIELD_ATTR +#include "opt_range.h" // SEL_ARG, null_element +#include "sql_type_fixedbin_storage.h" + +/***********************************************************************/ + + +template<class FbtImpl> +class FixedBinTypeBundle +{ +public: + class Fbt: public FbtImpl + { + protected: + using FbtImpl::m_buffer; + bool make_from_item(Item *item, bool warn) + { + if (item->type_handler() == type_handler_fbt()) + { + Native tmp(m_buffer, sizeof(m_buffer)); + bool rc= item->val_native(current_thd, &tmp); + if (rc) + return true; + DBUG_ASSERT(tmp.length() == sizeof(m_buffer)); + if (tmp.ptr() != m_buffer) + memcpy(m_buffer, tmp.ptr(), sizeof(m_buffer)); + return false; + } + StringBuffer<FbtImpl::max_char_length()+1> tmp; + String *str= item->val_str(&tmp); + return str ? make_from_character_or_binary_string(str, warn) : true; + } + + bool character_string_to_fbt(const char *str, size_t str_length, + CHARSET_INFO *cs) + { + if (cs->state & MY_CS_NONASCII) + { + char tmp[FbtImpl::max_char_length()+1]; + String_copier copier; + uint length= copier.well_formed_copy(&my_charset_latin1, tmp, sizeof(tmp), + cs, str, str_length); + return FbtImpl::ascii_to_fbt(tmp, length); + } + return FbtImpl::ascii_to_fbt(str, str_length); + } + bool make_from_character_or_binary_string(const String *str, bool warn) + { + if (str->charset() != &my_charset_bin) + { + bool rc= character_string_to_fbt(str->ptr(), str->length(), + str->charset()); + if (rc && warn) + current_thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, + type_handler_fbt()->name().ptr(), ErrConvString(str).ptr()); + return rc; + } + if (str->length() != sizeof(m_buffer)) + { + if (warn) + current_thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, + type_handler_fbt()->name().ptr(), ErrConvString(str).ptr()); + return true; + } + DBUG_ASSERT(str->ptr() != m_buffer); + memcpy(m_buffer, str->ptr(), sizeof(m_buffer)); + return false; + } + bool binary_to_fbt(const char *str, size_t length) + { + if (length != sizeof(m_buffer)) + return true; + memcpy(m_buffer, str, length); + return false; + } + + Fbt() { } + + public: + + static Fbt zero() + { + Fbt fbt; + fbt.set_zero(); + return fbt; + } + + static Fbt record_to_memory(const char *ptr) + { + Fbt fbt; + FbtImpl::record_to_memory(fbt.m_buffer, ptr); + return fbt; + } + /* + Check at Item's fix_fields() time if "item" can return a nullable value + on conversion to Fbt, or conversion produces a NOT NULL Fbt value. + */ + static bool fix_fields_maybe_null_on_conversion_to_fbt(Item *item) + { + if (item->maybe_null()) + return true; + if (item->type_handler() == type_handler_fbt()) + return false; + if (!item->const_item() || item->is_expensive()) + return true; + return Fbt_null(item, false).is_null(); + } + + public: + + Fbt(Item *item, bool *error, bool warn= true) + { + *error= make_from_item(item, warn); + } + void to_record(char *str, size_t str_size) const + { + DBUG_ASSERT(str_size >= sizeof(m_buffer)); + FbtImpl::memory_to_record(str, m_buffer); + } + bool to_binary(String *to) const + { + return to->copy(m_buffer, sizeof(m_buffer), &my_charset_bin); + } + bool to_native(Native *to) const + { + return to->copy(m_buffer, sizeof(m_buffer)); + } + bool to_string(String *to) const + { + to->set_charset(&my_charset_latin1); + if (to->alloc(FbtImpl::max_char_length()+1)) + return true; + to->length((uint32) FbtImpl::to_string(const_cast<char*>(to->ptr()), + FbtImpl::max_char_length()+1)); + return false; + } + int cmp(const Binary_string &other) const + { + return FbtImpl::cmp(FbtImpl::to_lex_cstring(), other.to_lex_cstring()); + } + int cmp(const Fbt &other) const + { + return FbtImpl::cmp(FbtImpl::to_lex_cstring(), other.to_lex_cstring()); + } + }; + + class Fbt_null: public Fbt, public Null_flag + { + public: + // Initialize from a text representation + Fbt_null(const char *str, size_t length, CHARSET_INFO *cs) + :Null_flag(Fbt::character_string_to_fbt(str, length, cs)) { } + Fbt_null(const String &str) + :Fbt_null(str.ptr(), str.length(), str.charset()) { } + // Initialize from a binary representation + Fbt_null(const char *str, size_t length) + :Null_flag(Fbt::binary_to_fbt(str, length)) { } + Fbt_null(const Binary_string &str) + :Fbt_null(str.ptr(), str.length()) { } + // Initialize from an Item + Fbt_null(Item *item, bool warn= true) + :Null_flag(Fbt::make_from_item(item, warn)) { } + public: + const Fbt& to_fbt() const + { + DBUG_ASSERT(!is_null()); + return *this; + } + void to_record(char *str, size_t str_size) const + { + to_fbt().to_record(str, str_size); + } + bool to_binary(String *to) const + { + return to_fbt().to_binary(to); + } + size_t to_string(char *dst, size_t dstsize) const + { + return to_fbt().to_string(dst, dstsize); + } + bool to_string(String *to) const + { + return to_fbt().to_string(to); + } + }; + + class Type_std_attributes_fbt: public Type_std_attributes + { + public: + Type_std_attributes_fbt() + :Type_std_attributes( + Type_numeric_attributes(FbtImpl::max_char_length(), 0, true), + DTCollation_numeric()) + { } + }; + + class Type_handler_fbt: public Type_handler + { + bool character_or_binary_string_to_native(THD *thd, const String *str, + Native *to) const + { + if (str->charset() == &my_charset_bin) + { + // Convert from a binary string + if (str->length() != FbtImpl::binary_length() || + to->copy(str->ptr(), str->length())) + { + thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, + name().ptr(), ErrConvString(str).ptr()); + return true; + } + return false; + } + // Convert from a character string + Fbt_null tmp(*str); + if (tmp.is_null()) + thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, + name().ptr(), ErrConvString(str).ptr()); + return tmp.is_null() || tmp.to_native(to); + } + + public: + ~Type_handler_fbt() override {} + + const Type_collection *type_collection() const override + { + static Type_collection_fbt type_collection_fbt; + return &type_collection_fbt; + } + + const Name &default_value() const override + { + return FbtImpl::default_value(); + } + ulong KEY_pack_flags(uint column_nr) const override + { + return FbtImpl::KEY_pack_flags(column_nr); + } + protocol_send_type_t protocol_send_type() const override + { + return PROTOCOL_SEND_STRING; + } + bool Item_append_extended_type_info(Send_field_extended_metadata *to, + const Item *item) const override + { + return to->set_data_type_name(name().lex_cstring()); + } + + enum_field_types field_type() const override + { + return MYSQL_TYPE_STRING; + } + + Item_result result_type() const override + { + return STRING_RESULT; + } + + Item_result cmp_type() const override + { + return STRING_RESULT; + } + + enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr) + const override + { + return DYN_COL_STRING; + } + + uint32 max_display_length_for_field(const Conv_source &src) const override + { + return FbtImpl::max_char_length(); + } + + const Type_handler *type_handler_for_comparison() const override + { + return this; + } + + int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const override + { + DBUG_ASSERT(field->type_handler() == this); + Fbt_null ni(item); // Convert Item to Fbt + if (ni.is_null()) + return 0; + NativeBuffer<FbtImpl::binary_length()+1> tmp; + if (field->val_native(&tmp)) + { + DBUG_ASSERT(0); + return 0; + } + return -ni.cmp(tmp); + } + CHARSET_INFO *charset_for_protocol(const Item *item) const override + { + return item->collation.collation; + } + + bool is_scalar_type() const override { return true; } + bool is_val_native_ready() const override { return true; } + bool can_return_int() const override { return false; } + bool can_return_decimal() const override { return false; } + bool can_return_real() const override { return false; } + bool can_return_str() const override { return true; } + bool can_return_text() const override { return true; } + bool can_return_date() const override { return false; } + bool can_return_time() const override { return false; } + bool convert_to_binary_using_val_native() const override { return true; } + + decimal_digits_t Item_time_precision(THD *thd, Item *item) const override + { + return 0; + } + decimal_digits_t Item_datetime_precision(THD *thd, Item *item) const override + { + return 0; + } + decimal_digits_t Item_decimal_scale(const Item *item) const override + { + return 0; + } + decimal_digits_t Item_decimal_precision(const Item *item) const override + { + /* This will be needed if we ever allow cast from Fbt to DECIMAL. */ + return (FbtImpl::binary_length()*8+7)/10*3; // = bytes to decimal digits + } + + /* + Returns how many digits a divisor adds into a division result. + See Item::divisor_precision_increment() in item.h for more comments. + */ + decimal_digits_t Item_divisor_precision_increment(const Item *) const override + { + return 0; + } + /** + Makes a temporary table Field to handle numeric aggregate functions, + e.g. SUM(DISTINCT expr), AVG(DISTINCT expr), etc. + */ + Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const override + { + DBUG_ASSERT(0); + return 0; + } + Field *make_conversion_table_field(MEM_ROOT *root, TABLE *table, uint metadata, + const Field *target) const override + { + const Record_addr tmp(NULL, Bit_addr(true)); + return new (table->in_use->mem_root) Field_fbt(&empty_clex_str, tmp); + } + // Fix attributes after the parser + bool Column_definition_fix_attributes(Column_definition *c) const override + { + c->length= FbtImpl::max_char_length(); + return false; + } + + bool Column_definition_prepare_stage1(THD *thd, MEM_ROOT *mem_root, + Column_definition *def, handler *file, + ulonglong table_flags, + const Column_derived_attributes *derived_attr) + const override + { + def->prepare_stage1_simple(&my_charset_numeric); + return false; + } + + bool Column_definition_redefine_stage1(Column_definition *def, + const Column_definition *dup, + const handler *file) const override + { + def->redefine_stage1_common(dup, file); + def->set_compression_method(dup->compression_method()); + def->create_length_to_internal_length_string(); + return false; + } + + bool Column_definition_prepare_stage2(Column_definition *def, handler *file, + ulonglong table_flags) const override + { + def->pack_flag= FIELDFLAG_BINARY; + return false; + } + + bool partition_field_check(const LEX_CSTRING &field_name, + Item *item_expr) const override + { + if (item_expr->cmp_type() != STRING_RESULT) + { + my_error(ER_WRONG_TYPE_COLUMN_VALUE_ERROR, MYF(0)); + return true; + } + return false; + } + + bool partition_field_append_value(String *to, Item *item_expr, + CHARSET_INFO *field_cs, + partition_value_print_mode_t mode) + const override + { + StringBuffer<FbtImpl::max_char_length()+64> fbtstr; + Fbt_null fbt(item_expr); + if (fbt.is_null()) + { + my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); + return true; + } + return fbt.to_string(&fbtstr) || + to->append('\'') || + to->append(fbtstr) || + to->append('\''); + } + + Field *make_table_field(MEM_ROOT *root, const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE_SHARE *table) const override + { + return new (root) Field_fbt(name, addr); + } + + Field * make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const override + { + return new (mem_root) Field_fbt(name, addr); + } + void Column_definition_attributes_frm_pack(const Column_definition_attributes *def, + uchar *buff) const override + { + def->frm_pack_basic(buff); + def->frm_pack_charset(buff); + } + bool Column_definition_attributes_frm_unpack(Column_definition_attributes *def, + TABLE_SHARE *share, const uchar *buffer, + LEX_CUSTRING *gis_options) + const override + { + def->frm_unpack_basic(buffer); + return def->frm_unpack_charset(share, buffer); + } + void make_sort_key_part(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field, + Sort_param *param) const override + { + DBUG_ASSERT(item->type_handler() == this); + NativeBuffer<FbtImpl::binary_length()+1> tmp; + item->val_native_result(current_thd, &tmp); + if (item->maybe_null()) + { + if (item->null_value) + { + memset(to, 0, FbtImpl::binary_length() + 1); + return; + } + *to++= 1; + } + DBUG_ASSERT(!item->null_value); + DBUG_ASSERT(FbtImpl::binary_length() == tmp.length()); + DBUG_ASSERT(FbtImpl::binary_length() == sort_field->length); + FbtImpl::memory_to_record((char*) to, tmp.ptr()); + } + uint make_packed_sort_key_part(uchar *to, Item *item, + const SORT_FIELD_ATTR *sort_field, + Sort_param *param) const override + { + DBUG_ASSERT(item->type_handler() == this); + NativeBuffer<FbtImpl::binary_length()+1> tmp; + item->val_native_result(current_thd, &tmp); + if (item->maybe_null()) + { + if (item->null_value) + { + *to++=0; + return 0; + } + *to++= 1; + } + DBUG_ASSERT(!item->null_value); + DBUG_ASSERT(FbtImpl::binary_length() == tmp.length()); + DBUG_ASSERT(FbtImpl::binary_length() == sort_field->length); + FbtImpl::memory_to_record((char*) to, tmp.ptr()); + return tmp.length(); + } + void sort_length(THD *thd, const Type_std_attributes *item, + SORT_FIELD_ATTR *attr) const override + { + attr->original_length= attr->length= FbtImpl::binary_length(); + attr->suffix_length= 0; + } + uint32 max_display_length(const Item *item) const override + { + return FbtImpl::max_char_length(); + } + uint32 calc_pack_length(uint32 length) const override + { + return FbtImpl::binary_length(); + } + void Item_update_null_value(Item *item) const override + { + NativeBuffer<FbtImpl::binary_length()+1> tmp; + item->val_native(current_thd, &tmp); + } + bool Item_save_in_value(THD *thd, Item *item, st_value *value) const override + { + value->m_type= DYN_COL_STRING; + String *str= item->val_str(&value->m_string); + if (str != &value->m_string && !item->null_value) + { + // "item" returned a non-NULL value + if (Fbt_null(*str).is_null()) + { + /* + The value was not-null, but conversion to FBT failed: + SELECT a, DECODE_ORACLE(fbtcol, 'garbage', '<NULL>', '::01', '01') + FROM t1; + */ + thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, + name().ptr(), ErrConvString(str).ptr()); + value->m_type= DYN_COL_NULL; + return true; + } + // "item" returned a non-NULL value, and it was a valid FBT + value->m_string.set(str->ptr(), str->length(), str->charset()); + } + return check_null(item, value); + } + void Item_param_setup_conversion(THD *thd, Item_param *param) const override + { + param->setup_conversion_string(thd, thd->variables.character_set_client); + } + void Item_param_set_param_func(Item_param *param, + uchar **pos, ulong len) const override + { + param->set_param_str(pos, len); + } + bool Item_param_set_from_value(THD *thd, Item_param *param, + const Type_all_attributes *attr, + const st_value *val) const override + { + param->unsigned_flag= false; + param->setup_conversion_string(thd, attr->collation.collation); + /* + Exact value of max_length is not known unless fbt is converted to + charset of connection, so we have to set it later. + */ + return param->set_str(val->m_string.ptr(), val->m_string.length(), + attr->collation.collation, + attr->collation.collation); + } + bool Item_param_val_native(THD *thd, Item_param *item, Native *to) + const override + { + StringBuffer<FbtImpl::max_char_length()+1> buffer; + String *str= item->val_str(&buffer); + if (!str) + return true; + Fbt_null tmp(*str); + return tmp.is_null() || tmp.to_native(to); + } + bool Item_send(Item *item, Protocol *p, st_value *buf) const override + { + return Item_send_str(item, p, buf); + } + int Item_save_in_field(Item *item, Field *field, bool no_conversions) + const override + { + if (field->type_handler() == this) + { + NativeBuffer<MAX_FIELD_WIDTH> tmp; + bool rc= item->val_native(current_thd, &tmp); + if (rc || item->null_value) + return set_field_to_null_with_conversions(field, no_conversions); + field->set_notnull(); + return field->store_native(tmp); + } + return item->save_str_in_field(field, no_conversions); + } + + String *print_item_value(THD *thd, Item *item, String *str) const override + { + StringBuffer<FbtImpl::max_char_length()+64> buf; + String *result= item->val_str(&buf); + /* + TODO: This should eventually use one of these notations: + 1. CAST('xxx' AS Fbt) + Problem: CAST is not supported as a NAME_CONST() argument. + 2. Fbt'xxx' + Problem: This syntax is not supported by the parser yet. + */ + return !result || str->realloc(result->length() + 2) || + str->append(STRING_WITH_LEN("'")) || + str->append(result->ptr(), result->length()) || + str->append(STRING_WITH_LEN("'")) ? nullptr : str; + } + + /** + Check if + WHERE expr=value AND expr=const + can be rewritten as: + WHERE const=value AND expr=const + + "this" is the comparison handler that is used by "target". + + @param target - the predicate expr=value, + whose "expr" argument will be replaced to "const". + @param target_expr - the target's "expr" which will be replaced to "const". + @param target_value - the target's second argument, it will remain unchanged. + @param source - the equality predicate expr=const (or expr<=>const) + that can be used to rewrite the "target" part + (under certain conditions, see the code). + @param source_expr - the source's "expr". It should be exactly equal to + the target's "expr" to make condition rewrite possible. + @param source_const - the source's "const" argument, it will be inserted + into "target" instead of "expr". + */ + bool can_change_cond_ref_to_const(Item_bool_func2 *target, Item *target_expr, + Item *target_value, Item_bool_func2 *source, + Item *source_expr, Item *source_const) + const override + { + /* + WHERE COALESCE(col)='xxx' AND COALESCE(col)=CONCAT(a); --> + WHERE COALESCE(col)='xxx' AND 'xxx'=CONCAT(a); + */ + return target->compare_type_handler() == source->compare_type_handler(); + } + bool subquery_type_allows_materialization(const Item *inner, + const Item *outer, bool) const override + { + /* + Example: + SELECT * FROM t1 WHERE a IN (SELECT col FROM t1 GROUP BY col); + Allow materialization only if the outer column is also FBT. + This can be changed for more relaxed rules in the future. + */ + DBUG_ASSERT(inner->type_handler() == this); + return outer->type_handler() == this; + } + /** + Make a simple constant replacement item for a constant "src", + so the new item can futher be used for comparison with "cmp", e.g.: + src = cmp -> replacement = cmp + + "this" is the type handler that is used to compare "src" and "cmp". + + @param thd - current thread, for mem_root + @param src - The item that we want to replace. It's a const item, + but it can be complex enough to calculate on every row. + @param cmp - The src's comparand. + @retval - a pointer to the created replacement Item + @retval - NULL, if could not create a replacement (e.g. on EOM). + NULL is also returned for ROWs, because instead of replacing + a Item_row to a new Item_row, Type_handler_row just replaces + its elements. + */ + Item *make_const_item_for_comparison(THD *thd, Item *src, + const Item *cmp) const override + { + Fbt_null tmp(src); + if (tmp.is_null()) + return new (thd->mem_root) Item_null(thd, src->name.str); + return new (thd->mem_root) Item_literal_fbt(thd, tmp); + } + Item_cache *Item_get_cache(THD *thd, const Item *item) const override + { + return new (thd->mem_root) Item_cache_fbt(thd); + } + + Item *create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const override + { + return new (thd->mem_root) Item_typecast_fbt(thd, item); + } + Item_copy *create_item_copy(THD *thd, Item *item) const override + { + return new (thd->mem_root) Item_copy_fbt(thd, item); + } + int cmp_native(const Native &a, const Native &b) const override + { + return FbtImpl::cmp(a.to_lex_cstring(), b.to_lex_cstring()); + } + bool set_comparator_func(THD *thd, Arg_comparator *cmp) const override + { + return cmp->set_cmp_func_native(thd); + } + bool Item_const_eq(const Item_const *a, const Item_const *b, + bool binary_cmp) const override + { + return false; + } + bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr, + Item *a, Item *b) const override + { + Fbt_null na(a), nb(b); + return !na.is_null() && !nb.is_null() && !na.cmp(nb); + } + bool Item_hybrid_func_fix_attributes(THD *thd, const LEX_CSTRING &name, + Type_handler_hybrid_field_type *h, + Type_all_attributes *attr, + Item **items, uint nitems) const override + { + attr->Type_std_attributes::operator=(Type_std_attributes_fbt()); + h->set_handler(this); + /* + If some of the arguments cannot be safely converted to "FBT NOT NULL", + then mark the entire function nullability as NULL-able. + Otherwise, keep the generic nullability calculated by earlier stages: + - either by the most generic way in Item_func::fix_fields() + - or by Item_func_xxx::fix_length_and_dec() before the call of + Item_hybrid_func_fix_attributes() + IFNULL() is special. It does not need to test args[0]. + */ + uint first= dynamic_cast<Item_func_ifnull*>(attr) ? 1 : 0; + for (uint i= first; i < nitems; i++) + { + if (Fbt::fix_fields_maybe_null_on_conversion_to_fbt(items[i])) + { + attr->set_type_maybe_null(true); + break; + } + } + return false; + } + bool Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, + Item **items, uint nitems) const override + { + return Item_hybrid_func_fix_attributes(thd, func->func_name_cstring(), + func, func, items, nitems); + + } + bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const override + { + func->Type_std_attributes::operator=(Type_std_attributes_fbt()); + func->set_handler(this); + return false; + } + bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *func) const override + { + return Item_func_or_sum_illegal_param(func); + } + bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *func) const override + { + return Item_func_or_sum_illegal_param(func); + } + bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *func) const override + { + return Item_func_or_sum_illegal_param(func); + } + + bool Item_val_native_with_conversion(THD *thd, Item *item, + Native *to) const override + { + if (item->type_handler() == this) + return item->val_native(thd, to); // No conversion needed + StringBuffer<FbtImpl::max_char_length()+1> buffer; + String *str= item->val_str(&buffer); + return str ? character_or_binary_string_to_native(thd, str, to) : true; + } + bool Item_val_native_with_conversion_result(THD *thd, Item *item, + Native *to) const override + { + if (item->type_handler() == this) + return item->val_native_result(thd, to); // No conversion needed + StringBuffer<FbtImpl::max_char_length()+1> buffer; + String *str= item->str_result(&buffer); + return str ? character_or_binary_string_to_native(thd, str, to) : true; + } + + bool Item_val_bool(Item *item) const override + { + NativeBuffer<FbtImpl::binary_length()+1> tmp; + if (item->val_native(current_thd, &tmp)) + return false; + return !Fbt::only_zero_bytes(tmp.ptr(), tmp.length()); + } + void Item_get_date(THD *thd, Item *item, Temporal::Warn *buff, + MYSQL_TIME *ltime, date_mode_t fuzzydate) const override + { + set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); + } + + longlong Item_val_int_signed_typecast(Item *item) const override + { + DBUG_ASSERT(0); + return 0; + } + + longlong Item_val_int_unsigned_typecast(Item *item) const override + { + DBUG_ASSERT(0); + return 0; + } + + String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) + const override + { + NativeBuffer<FbtImpl::binary_length()+1> tmp; + if ((item->null_value= item->arguments()[0]->val_native(current_thd, &tmp))) + return nullptr; + DBUG_ASSERT(tmp.length() == FbtImpl::binary_length()); + if (str->set_hex(tmp.ptr(), tmp.length())) + { + str->length(0); + str->set_charset(item->collation.collation); + } + return str; + } + + String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *item, + String *str) const override + { + NativeBuffer<FbtImpl::binary_length()+1> native; + if (item->val_native(current_thd, &native)) + { + DBUG_ASSERT(item->null_value); + return nullptr; + } + DBUG_ASSERT(native.length() == FbtImpl::binary_length()); + Fbt_null tmp(native.ptr(), native.length()); + return tmp.is_null() || tmp.to_string(str) ? nullptr : str; + } + double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *) + const override + { + return 0; + } + longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *) + const override + { + return 0; + } + my_decimal * + Item_func_hybrid_field_type_val_decimal(Item_func_hybrid_field_type *, + my_decimal *to) const override + { + my_decimal_set_zero(to); + return to; + } + void Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *, + Temporal::Warn *, + MYSQL_TIME *to, + date_mode_t fuzzydate) + const override + { + set_zero_time(to, MYSQL_TIMESTAMP_TIME); + } + // WHERE is Item_func_min_max_val_native??? + String *Item_func_min_max_val_str(Item_func_min_max *func, String *str) + const override + { + Fbt_null tmp(func); + return tmp.is_null() || tmp.to_string(str) ? nullptr : str; + } + double Item_func_min_max_val_real(Item_func_min_max *) const override + { + return 0; + } + longlong Item_func_min_max_val_int(Item_func_min_max *) const override + { + return 0; + } + my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, + my_decimal *to) const override + { + my_decimal_set_zero(to); + return to; + } + bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*, MYSQL_TIME *to, + date_mode_t fuzzydate) const override + { + set_zero_time(to, MYSQL_TIMESTAMP_TIME); + return false; + } + + bool Item_func_between_fix_length_and_dec(Item_func_between *func) const override + { + return false; + } + longlong Item_func_between_val_int(Item_func_between *func) const override + { + return func->val_int_cmp_native(); + } + + cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const override + { + return new (thd->mem_root) cmp_item_fbt; + } + + in_vector *make_in_vector(THD *thd, const Item_func_in *func, + uint nargs) const override + { + return new (thd->mem_root) in_fbt(thd, nargs); + } + + bool Item_func_in_fix_comparator_compatible_types(THD *thd, + Item_func_in *func) + const override + { + if (func->compatible_types_scalar_bisection_possible()) + { + return func->value_list_convert_const_to_int(thd) || + func->fix_for_scalar_comparison_using_bisection(thd); + } + return + func->fix_for_scalar_comparison_using_cmp_items(thd, + 1U << (uint) STRING_RESULT); + } + bool Item_func_round_fix_length_and_dec(Item_func_round *func) const override + { + return Item_func_or_sum_illegal_param(func); + } + bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *func) const override + { + return Item_func_or_sum_illegal_param(func); + } + + bool Item_func_abs_fix_length_and_dec(Item_func_abs *func) const override + { + return Item_func_or_sum_illegal_param(func); + } + + bool Item_func_neg_fix_length_and_dec(Item_func_neg *func) const override + { + return Item_func_or_sum_illegal_param(func); + } + + bool Item_func_signed_fix_length_and_dec(Item_func_signed *item) const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_double_typecast_fix_length_and_dec(Item_double_typecast *item) + const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_float_typecast_fix_length_and_dec(Item_float_typecast *item) + const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *item) + const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *item) + const override + { + if (item->cast_charset() == &my_charset_bin) + { + static Item_char_typecast_func_handler_fbt_to_binary + item_char_typecast_func_handler_fbt_to_binary; + item->fix_length_and_dec_native_to_binary(FbtImpl::binary_length()); + item->set_func_handler(&item_char_typecast_func_handler_fbt_to_binary); + return false; + } + item->fix_length_and_dec_str(); + return false; + } + + bool Item_time_typecast_fix_length_and_dec(Item_time_typecast *item) const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_date_typecast_fix_length_and_dec(Item_date_typecast *item) const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *item) + const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_func_plus_fix_length_and_dec(Item_func_plus *item) const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_func_minus_fix_length_and_dec(Item_func_minus *item) const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_func_mul_fix_length_and_dec(Item_func_mul *item) const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_func_div_fix_length_and_dec(Item_func_div *item) const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_func_mod_fix_length_and_dec(Item_func_mod *item) const override + { + return Item_func_or_sum_illegal_param(item); + } + }; + + class cmp_item_fbt: public cmp_item_scalar + { + Fbt m_native; + public: + cmp_item_fbt() + :cmp_item_scalar(), + m_native(Fbt::zero()) + { } + void store_value(Item *item) override + { + m_native= Fbt(item, &m_null_value); + } + int cmp_not_null(const Value *val) override + { + DBUG_ASSERT(!val->is_null()); + DBUG_ASSERT(val->is_string()); + Fbt_null tmp(val->m_string); + DBUG_ASSERT(!tmp.is_null()); + return m_native.cmp(tmp); + } + int cmp(Item *arg) override + { + Fbt_null tmp(arg); + return m_null_value || tmp.is_null() ? UNKNOWN : m_native.cmp(tmp) != 0; + } + int compare(cmp_item *ci) override + { + cmp_item_fbt *tmp= static_cast<cmp_item_fbt*>(ci); + DBUG_ASSERT(!m_null_value); + DBUG_ASSERT(!tmp->m_null_value); + return m_native.cmp(tmp->m_native); + } + cmp_item *make_same(THD *thd) override + { + return new (thd->mem_root) cmp_item_fbt(); + } + }; + + class Field_fbt: public Field + { + static void set_min_value(char *ptr) + { + memset(ptr, 0, FbtImpl::binary_length()); + } + static void set_max_value(char *ptr) + { + memset(ptr, 0xFF, FbtImpl::binary_length()); + } + void store_warning(const ErrConv &str, + Sql_condition::enum_warning_level level) + { + if (get_thd()->count_cuted_fields <= CHECK_FIELD_EXPRESSION) + return; + const TABLE_SHARE *s= table->s; + static const Name type_name= type_handler_fbt()->name(); + get_thd()->push_warning_truncated_value_for_field(level, type_name.ptr(), + str.ptr(), s ? s->db.str : nullptr, s ? s->table_name.str : nullptr, + field_name.str); + } + int set_null_with_warn(const ErrConv &str) + { + store_warning(str, Sql_condition::WARN_LEVEL_WARN); + set_null(); + return 1; + } + int set_min_value_with_warn(const ErrConv &str) + { + store_warning(str, Sql_condition::WARN_LEVEL_WARN); + set_min_value((char*) ptr); + return 1; + } + int set_max_value_with_warn(const ErrConv &str) + { + store_warning(str, Sql_condition::WARN_LEVEL_WARN); + set_max_value((char*) ptr); + return 1; + } + int store_fbt_null_with_warn(const Fbt_null &fbt, + const ErrConvString &err) + { + DBUG_ASSERT(marked_for_write_or_computed()); + if (fbt.is_null()) + return maybe_null() ? set_null_with_warn(err) + : set_min_value_with_warn(err); + fbt.to_record((char *) ptr, FbtImpl::binary_length()); + return 0; + } + + public: + Field_fbt(const LEX_CSTRING *field_name_arg, const Record_addr &rec) + :Field(rec.ptr(), FbtImpl::max_char_length(), + rec.null_ptr(), rec.null_bit(), Field::NONE, field_name_arg) + { + flags|= BINARY_FLAG | UNSIGNED_FLAG; + } + const Type_handler *type_handler() const override + { + return type_handler_fbt(); + } + uint32 max_display_length() const override { return field_length; } + bool str_needs_quotes() const override { return true; } + const DTCollation &dtcollation() const override + { + static DTCollation_numeric c; + return c; + } + CHARSET_INFO *charset(void) const override { return &my_charset_numeric; } + const CHARSET_INFO *sort_charset(void) const override { return &my_charset_bin; } + /** + This makes client-server protocol convert the value according + to @@character_set_client. + */ + bool binary() const override { return false; } + enum ha_base_keytype key_type() const override { return HA_KEYTYPE_BINARY; } + + bool is_equal(const Column_definition &new_field) const override + { + return new_field.type_handler() == type_handler(); + } + bool eq_def(const Field *field) const override + { + return Field::eq_def(field); + } + double pos_in_interval(Field *min, Field *max) override + { + return pos_in_interval_val_str(min, max, 0); + } + int cmp(const uchar *a, const uchar *b) const override + { return memcmp(a, b, pack_length()); } + + void sort_string(uchar *to, uint length) override + { + DBUG_ASSERT(length == pack_length()); + memcpy(to, ptr, length); + } + uint32 pack_length() const override + { + return FbtImpl::binary_length(); + } + uint pack_length_from_metadata(uint field_metadata) const override + { + return FbtImpl::binary_length(); + } + + void sql_type(String &str) const override + { + static Name name= type_handler_fbt()->name(); + str.set_ascii(name.ptr(), name.length()); + } + + void make_send_field(Send_field *to) override + { + Field::make_send_field(to); + to->set_data_type_name(type_handler_fbt()->name().lex_cstring()); + } + + bool validate_value_in_record(THD *thd, const uchar *record) const override + { + return false; + } + + bool val_native(Native *to) override + { + DBUG_ASSERT(marked_for_read()); + DBUG_ASSERT(!is_null()); + if (to->alloc(FbtImpl::binary_length())) + return true; + to->length(FbtImpl::binary_length()); + FbtImpl::record_to_memory((char*) to->ptr(), (const char*) ptr); + return false; + } + + Fbt to_fbt() const + { + DBUG_ASSERT(marked_for_read()); + DBUG_ASSERT(!is_null()); + return Fbt::record_to_memory((const char*) ptr); + } + + String *val_str(String *val_buffer, String *) override + { + return to_fbt().to_string(val_buffer) ? NULL : val_buffer; + } + + my_decimal *val_decimal(my_decimal *to) override + { + DBUG_ASSERT(marked_for_read()); + my_decimal_set_zero(to); + return to; + } + + longlong val_int() override + { + DBUG_ASSERT(marked_for_read()); + return 0; + } + + double val_real() override + { + DBUG_ASSERT(marked_for_read()); + return 0; + } + + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override + { + DBUG_ASSERT(marked_for_read()); + set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); + return false; + } + + bool val_bool(void) override + { + DBUG_ASSERT(marked_for_read()); + return !Fbt::only_zero_bytes((const char *) ptr, FbtImpl::binary_length()); + } + + int store_native(const Native &value) override + { + DBUG_ASSERT(marked_for_write_or_computed()); + DBUG_ASSERT(value.length() == FbtImpl::binary_length()); + FbtImpl::memory_to_record((char*) ptr, value.ptr()); + return 0; + } + + int store(const char *str, size_t length, CHARSET_INFO *cs) override + { + return cs == &my_charset_bin ? store_binary(str, length) + : store_text(str, length, cs); + } + + int store_text(const char *str, size_t length, CHARSET_INFO *cs) override + { + return store_fbt_null_with_warn(Fbt_null(str, length, cs), + ErrConvString(str, length, cs)); + } + + int store_binary(const char *str, size_t length) override + { + return store_fbt_null_with_warn(Fbt_null(str, length), + ErrConvString(str, length, + &my_charset_bin)); + } + + int store_hex_hybrid(const char *str, size_t length) override + { + return Field_fbt::store_binary(str, length); + } + + int store_decimal(const my_decimal *num) override + { + DBUG_ASSERT(marked_for_write_or_computed()); + return set_min_value_with_warn(ErrConvDecimal(num)); + } + + int store(longlong nr, bool unsigned_flag) override + { + DBUG_ASSERT(marked_for_write_or_computed()); + return set_min_value_with_warn( + ErrConvInteger(Longlong_hybrid(nr, unsigned_flag))); + } + + int store(double nr) override + { + DBUG_ASSERT(marked_for_write_or_computed()); + return set_min_value_with_warn(ErrConvDouble(nr)); + } + + int store_time_dec(const MYSQL_TIME *ltime, uint dec) override + { + DBUG_ASSERT(marked_for_write_or_computed()); + return set_min_value_with_warn(ErrConvTime(ltime)); + } + + /*** Field conversion routines ***/ + int store_field(Field *from) override + { + // INSERT INTO t1 (fbt_field) SELECT different_field_type FROM t2; + return from->save_in_field(this); + } + int save_in_field(Field *to) override + { + // INSERT INTO t2 (different_field_type) SELECT fbt_field FROM t1; + if (to->charset() == &my_charset_bin && + dynamic_cast<const Type_handler_general_purpose_string*> + (to->type_handler())) + { + NativeBuffer<FbtImpl::binary_length()+1> res; + val_native(&res); + return to->store(res.ptr(), res.length(), &my_charset_bin); + } + return save_in_field_str(to); + } + Copy_func *get_copy_func(const Field *from) const override + { + // ALTER to FBT from another field + return do_field_string; + } + + Copy_func *get_copy_func_to(const Field *to) const override + { + if (type_handler() == to->type_handler()) + { + // ALTER from FBT to FBT + DBUG_ASSERT(pack_length() == to->pack_length()); + DBUG_ASSERT(charset() == to->charset()); + DBUG_ASSERT(sort_charset() == to->sort_charset()); + return Field::do_field_eq; + } + // ALTER from FBT to another fbt type + if (to->charset() == &my_charset_bin && + dynamic_cast<const Type_handler_general_purpose_string*> + (to->type_handler())) + { + /* + ALTER from FBT to a binary string type, e.g.: + BINARY, TINYBLOB, BLOB, MEDIUMBLOB, LONGBLOB + */ + return do_field_fbt_native_to_binary; + } + return do_field_string; + } + + static void do_field_fbt_native_to_binary(Copy_field *copy) + { + NativeBuffer<FbtImpl::binary_length()+1> res; + copy->from_field->val_native(&res); + copy->to_field->store(res.ptr(), res.length(), &my_charset_bin); + } + + bool memcpy_field_possible(const Field *from) const override + { + // INSERT INTO t1 (fbt_field) SELECT field2 FROM t2; + return type_handler() == from->type_handler(); + } + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const override + { + if (type_handler() == source.type_handler() || + (source.type_handler() == &type_handler_string && + source.type_handler()->max_display_length_for_field(source) == + FbtImpl::binary_length())) + return rpl_conv_type_from_same_data_type(source.metadata(), rli, param); + return CONV_TYPE_IMPOSSIBLE; + } + + /*** Optimizer routines ***/ + bool test_if_equality_guarantees_uniqueness(const Item *const_item) const override + { + /* + This condition: + WHERE fbt_field=const + should return a single distinct value only, + as comparison is done according to FBT. + */ + return true; + } + bool can_be_substituted_to_equal_item(const Context &ctx, + const Item_equal *item_equal) + override + { + switch (ctx.subst_constraint()) { + case ANY_SUBST: + return ctx.compare_type_handler() == item_equal->compare_type_handler(); + case IDENTITY_SUBST: + return true; + } + return false; + } + Item *get_equal_const_item(THD *thd, const Context &ctx, + Item *const_item) override + { + Fbt_null tmp(const_item); + if (tmp.is_null()) + return NULL; + return new (thd->mem_root) Item_literal_fbt(thd, tmp); + } + bool can_optimize_keypart_ref(const Item_bool_func *cond, + const Item *item) const override + { + /* + Mixing of two different non-traditional types is currently prevented. + This may change in the future. + */ + DBUG_ASSERT(item->type_handler()->is_traditional_scalar_type() || + item->type_handler() == type_handler()); + return true; + } + /** + Test if Field can use range optimizer for a standard comparison operation: + <=, <, =, <=>, >, >= + Note, this method does not cover spatial operations. + */ + bool can_optimize_range(const Item_bool_func *cond, + const Item *item, + bool is_eq_func) const override + { + // See the DBUG_ASSERT comment in can_optimize_keypart_ref() + DBUG_ASSERT(item->type_handler()->is_traditional_scalar_type() || + item->type_handler() == type_handler()); + return true; + } + void hash(ulong *nr, ulong *nr2) override + { + if (is_null()) + *nr^= (*nr << 1) | 1; + else + FbtImpl::hash_record(ptr, nr, nr2); + } + SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *prm, KEY_PART *key_part, + const Item_bool_func *cond, + scalar_comparison_op op, Item *value) override + { + DBUG_ENTER("Field_fbt::get_mm_leaf"); + if (!can_optimize_scalar_range(prm, key_part, cond, op, value)) + DBUG_RETURN(0); + int err= value->save_in_field_no_warnings(this, 1); + if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0) + DBUG_RETURN(&null_element); + if (err > 0) + { + if (op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL) + DBUG_RETURN(new (prm->mem_root) SEL_ARG_IMPOSSIBLE(this)); + DBUG_RETURN(NULL); /* Cannot infer anything */ + } + DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value)); + } + bool can_optimize_hash_join(const Item_bool_func *cond, + const Item *item) const override + { + return can_optimize_keypart_ref(cond, item); + } + bool can_optimize_group_min_max(const Item_bool_func *cond, + const Item *const_item) const override + { + return true; + } + + uint row_pack_length() const override { return pack_length(); } + + Binlog_type_info binlog_type_info() const override + { + DBUG_ASSERT(type() == binlog_type()); + return Binlog_type_info_fixed_string(Field_fbt::binlog_type(), + FbtImpl::binary_length(), &my_charset_bin); + } + + uchar *pack(uchar *to, const uchar *from, uint max_length) override + { + DBUG_PRINT("debug", ("Packing field '%s'", field_name.str)); + return FbtImpl::pack(to, from, max_length); + } + + const uchar *unpack(uchar *to, const uchar *from, const uchar *from_end, + uint param_data) override + { + return FbtImpl::unpack(to, from, from_end, param_data); + } + + uint max_packed_col_length(uint max_length) override + { + return StringPack::max_packed_col_length(max_length); + } + + uint packed_col_length(const uchar *fbt_ptr, uint length) override + { + return StringPack::packed_col_length(fbt_ptr, length); + } + + uint size_of() const override { return sizeof(*this); } + }; + + class Item_typecast_fbt: public Item_func + { + public: + Item_typecast_fbt(THD *thd, Item *a) :Item_func(thd, a) {} + + const Type_handler *type_handler() const override + { return type_handler_fbt(); } + + enum Functype functype() const override { return CHAR_TYPECAST_FUNC; } + bool eq(const Item *item, bool binary_cmp) const override + { + if (this == item) + return true; + if (item->type() != FUNC_ITEM || + functype() != ((Item_func*)item)->functype()) + return false; + if (type_handler() != item->type_handler()) + return false; + Item_typecast_fbt *cast= (Item_typecast_fbt*) item; + return args[0]->eq(cast->args[0], binary_cmp); + } + LEX_CSTRING func_name_cstring() const override + { + static Name name= type_handler_fbt()->name(); + size_t len= 9+name.length()+1; + char *buf= (char*)current_thd->alloc(len); + strmov(strmov(buf, "cast_as_"), name.ptr()); + return { buf, len }; + } + void print(String *str, enum_query_type query_type) override + { + str->append(STRING_WITH_LEN("cast(")); + args[0]->print(str, query_type); + str->append(STRING_WITH_LEN(" as ")); + str->append(type_handler_fbt()->name().lex_cstring()); + str->append(')'); + } + bool fix_length_and_dec() override + { + Type_std_attributes::operator=(Type_std_attributes_fbt()); + if (Fbt::fix_fields_maybe_null_on_conversion_to_fbt(args[0])) + set_maybe_null(); + return false; + } + String *val_str(String *to) override + { + Fbt_null tmp(args[0]); + return (null_value= tmp.is_null() || tmp.to_string(to)) ? NULL : to; + } + longlong val_int() override + { + return 0; + } + double val_real() override + { + return 0; + } + my_decimal *val_decimal(my_decimal *to) override + { + my_decimal_set_zero(to); + return to; + } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override + { + set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); + return false; + } + bool val_native(THD *thd, Native *to) override + { + Fbt_null tmp(args[0]); + return null_value= tmp.is_null() || tmp.to_native(to); + } + Item *get_copy(THD *thd) override + { return get_item_copy<Item_typecast_fbt>(thd, this); } + }; + + class Item_cache_fbt: public Item_cache + { + NativeBuffer<FbtImpl::binary_length()+1> m_value; + public: + Item_cache_fbt(THD *thd) + :Item_cache(thd, type_handler_fbt()) { } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_fbt>(thd, this); } + bool cache_value() + { + if (!example) + return false; + value_cached= true; + null_value= example->val_native_with_conversion_result(current_thd, + &m_value, type_handler()); + return true; + } + String* val_str(String *to) + { + if (!has_value()) + return NULL; + Fbt_null tmp(m_value.ptr(), m_value.length()); + return tmp.is_null() || tmp.to_string(to) ? NULL : to; + } + my_decimal *val_decimal(my_decimal *to) + { + if (!has_value()) + return NULL; + my_decimal_set_zero(to); + return to; + } + longlong val_int() + { + if (!has_value()) + return 0; + return 0; + } + double val_real() + { + if (!has_value()) + return 0; + return 0; + } + longlong val_datetime_packed(THD *thd) + { + DBUG_ASSERT(0); + if (!has_value()) + return 0; + return 0; + } + longlong val_time_packed(THD *thd) + { + DBUG_ASSERT(0); + if (!has_value()) + return 0; + return 0; + } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { + if (!has_value()) + return true; + set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); + return false; + } + bool val_native(THD *thd, Native *to) + { + if (!has_value()) + return true; + return to->copy(m_value.ptr(), m_value.length()); + } + }; + + class Item_literal_fbt: public Item_literal + { + Fbt m_value; + public: + Item_literal_fbt(THD *thd) + :Item_literal(thd), + m_value(Fbt::zero()) + { } + Item_literal_fbt(THD *thd, const Fbt &value) + :Item_literal(thd), + m_value(value) + { } + const Type_handler *type_handler() const override + { + return type_handler_fbt(); + } + longlong val_int() override + { + return 0; + } + double val_real() override + { + return 0; + } + String *val_str(String *to) override + { + return m_value.to_string(to) ? NULL : to; + } + my_decimal *val_decimal(my_decimal *to) override + { + my_decimal_set_zero(to); + return to; + } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override + { + set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); + return false; + } + bool val_native(THD *thd, Native *to) override + { + return m_value.to_native(to); + } + void print(String *str, enum_query_type query_type) override + { + StringBuffer<FbtImpl::max_char_length()+64> tmp; + tmp.append(type_handler_fbt()->name().lex_cstring()); + my_caseup_str(&my_charset_latin1, tmp.c_ptr()); + str->append(tmp); + str->append('\''); + m_value.to_string(&tmp); + str->append(tmp); + str->append('\''); + } + Item *get_copy(THD *thd) override + { return get_item_copy<Item_literal_fbt>(thd, this); } + + // Non-overriding methods + void set_value(const Fbt &value) + { + m_value= value; + } + }; + + class Item_copy_fbt: public Item_copy + { + NativeBuffer<Fbt::binary_length()+1> m_value; + public: + Item_copy_fbt(THD *thd, Item *item_arg): Item_copy(thd, item_arg) {} + + bool val_native(THD *thd, Native *to) override + { + if (null_value) + return true; + return to->copy(m_value.ptr(), m_value.length()); + } + String *val_str(String *to) override + { + if (null_value) + return NULL; + Fbt_null tmp(m_value.ptr(), m_value.length()); + return tmp.is_null() || tmp.to_string(to) ? NULL : to; + } + my_decimal *val_decimal(my_decimal *to) override + { + my_decimal_set_zero(to); + return to; + } + double val_real() override + { + return 0; + } + longlong val_int() override + { + return 0; + } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override + { + set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); + return null_value; + } + void copy() override + { + null_value= item->val_native(current_thd, &m_value); + DBUG_ASSERT(null_value == item->null_value); + } + int save_in_field(Field *field, bool no_conversions) override + { + return Item::save_in_field(field, no_conversions); + } + Item *get_copy(THD *thd) override + { return get_item_copy<Item_copy_fbt>(thd, this); } + }; + + class in_fbt :public in_vector + { + Fbt m_value; + static int cmp_fbt(void *cmp_arg, Fbt *a, Fbt *b) + { + return a->cmp(*b); + } + public: + in_fbt(THD *thd, uint elements) + :in_vector(thd, elements, sizeof(Fbt), (qsort2_cmp) cmp_fbt, 0), + m_value(Fbt::zero()) + { } + const Type_handler *type_handler() const override + { + return type_handler_fbt(); + } + void set(uint pos, Item *item) override + { + Fbt *buff= &((Fbt *) base)[pos]; + Fbt_null value(item); + if (value.is_null()) + *buff= Fbt::zero(); + else + *buff= value; + } + uchar *get_value(Item *item) override + { + Fbt_null value(item); + if (value.is_null()) + return 0; + m_value= value; + return (uchar *) &m_value; + } + Item* create_item(THD *thd) override + { + return new (thd->mem_root) Item_literal_fbt(thd); + } + void value_to_item(uint pos, Item *item) override + { + const Fbt &buff= (((Fbt*) base)[pos]); + static_cast<Item_literal_fbt*>(item)->set_value(buff); + } + }; + + class Item_char_typecast_func_handler_fbt_to_binary: + public Item_handled_func::Handler_str + { + public: + const Type_handler *return_type_handler(const Item_handled_func *item) + const override + { + if (item->max_length > MAX_FIELD_VARCHARLENGTH) + return Type_handler::blob_type_handler(item->max_length); + if (item->max_length > 255) + return &type_handler_varchar; + return &type_handler_string; + } + bool fix_length_and_dec(Item_handled_func *xitem) const override + { + return false; + } + String *val_str(Item_handled_func *item, String *to) const override + { + DBUG_ASSERT(dynamic_cast<const Item_char_typecast*>(item)); + return static_cast<Item_char_typecast*>(item)-> + val_str_binary_from_native(to); + } + }; + + class Type_collection_fbt: public Type_collection + { + const Type_handler *aggregate_common(const Type_handler *a, + const Type_handler *b) const + { + if (a == b) + return a; + return NULL; + } + const Type_handler *aggregate_if_string(const Type_handler *a, + const Type_handler *b) const + { + static const Type_aggregator::Pair agg[]= + { + {type_handler_fbt(), &type_handler_null, type_handler_fbt()}, + {type_handler_fbt(), &type_handler_varchar, type_handler_fbt()}, + {type_handler_fbt(), &type_handler_string, type_handler_fbt()}, + {type_handler_fbt(), &type_handler_tiny_blob, type_handler_fbt()}, + {type_handler_fbt(), &type_handler_blob, type_handler_fbt()}, + {type_handler_fbt(), &type_handler_medium_blob, type_handler_fbt()}, + {type_handler_fbt(), &type_handler_long_blob, type_handler_fbt()}, + {type_handler_fbt(), &type_handler_hex_hybrid, type_handler_fbt()}, + {NULL,NULL,NULL} + }; + return Type_aggregator::find_handler_in_array(agg, a, b, true); + } + public: + const Type_handler *aggregate_for_result(const Type_handler *a, + const Type_handler *b) + const override + { + const Type_handler *h; + if ((h= aggregate_common(a, b)) || + (h= aggregate_if_string(a, b))) + return h; + return NULL; + } + + const Type_handler *aggregate_for_min_max(const Type_handler *a, + const Type_handler *b) + const override + { + return aggregate_for_result(a, b); + } + + const Type_handler *aggregate_for_comparison(const Type_handler *a, + const Type_handler *b) + const override + { + if (const Type_handler *h= aggregate_common(a, b)) + return h; + static const Type_aggregator::Pair agg[]= + { + {type_handler_fbt(), &type_handler_null, type_handler_fbt()}, + {type_handler_fbt(), &type_handler_long_blob, type_handler_fbt()}, + {NULL,NULL,NULL} + }; + return Type_aggregator::find_handler_in_array(agg, a, b, true); + } + + const Type_handler *aggregate_for_num_op(const Type_handler *a, + const Type_handler *b) + const override + { + return NULL; + } + + const Type_handler *handler_by_name(const LEX_CSTRING &name) const override + { + if (type_handler_fbt()->name().eq(name)) + return type_handler_fbt(); + return NULL; + } + }; + static Type_handler_fbt *type_handler_fbt() + { + static Type_handler_fbt th; + return &th; + } +}; + +#endif /* SQL_TYPE_FIXEDBIN_H */ diff --git a/sql/sql_type_fixedbin_storage.h b/sql/sql_type_fixedbin_storage.h new file mode 100644 index 00000000000..6e18335bd4c --- /dev/null +++ b/sql/sql_type_fixedbin_storage.h @@ -0,0 +1,173 @@ +#ifndef SQL_TYPE_FIXEDBIN_STORAGE +#define SQL_TYPE_FIXEDBIN_STORAGE +/* Copyright (c) 2019,2021 MariaDB Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +/* + This is a common code for plugin (?) types that are generally + handled like strings, but have their own fixed size on-disk binary storage + format and their own (variable size) canonical string representation. + + Examples are INET6 and UUID types. + + The MariaDB server uses three binary representations of a data type: + + 1. In-memory binary representation (user visible) + This representation: + - can be used in INSERT..VALUES (X'AABBCC') + - can be used in WHERE conditions: WHERE c1=X'AABBCC' + - is returned by CAST(x AS BINARY(N)) + - is returned by Field::val_native() and Item::val_native() + + 2. In-record binary representation (user invisible) + This representation: + - is used in records (is pointed by Field::ptr) + - must be comparable by memcmp() + + 3. Binlog binary (row) representation + Usually, for string data types the binlog representation + is based on the in-record representation with trailing byte compression: + - trailing space compression for text string data types + - trailing zero compression for binary string data types + + We have to have separate in-memory and in-record representations + because we use HA_KEYTYPE_BINARY for indexing. The engine API + does not have a way to pass a comparison function as a parameter. + + The default implementation below assumes that: + - the in-memory and in-record representations are equal + - the binlog representation is compatible with BINARY(N) + This is OK for simple data types, like INET6. + + Data type implementations that need different representations + can override the default implementation (like e.g. UUID does). +*/ + +/***********************************************************************/ + +template<size_t NATIVE_LEN, size_t MAX_CHAR_LEN> +class FixedBinTypeStorage +{ +protected: + // The buffer that stores the in-memory binary representation + char m_buffer[NATIVE_LEN]; + + // Non-initializing constructor + FixedBinTypeStorage() + { } + + FixedBinTypeStorage & set_zero() + { + bzero(&m_buffer, sizeof(m_buffer)); + return *this; + } +public: + + // Initialize from the in-memory binary representation + FixedBinTypeStorage(const char *str, size_t length) + { + if (length != binary_length()) + set_zero(); + else + memcpy(&m_buffer, str, sizeof(m_buffer)); + } + + // Return the buffer with the in-memory representation + Lex_cstring to_lex_cstring() const + { + return Lex_cstring(m_buffer, sizeof(m_buffer)); + } + + static constexpr uint binary_length() { return NATIVE_LEN; } + static constexpr uint max_char_length() { return MAX_CHAR_LEN; } + + // Compare the in-memory binary representations of two values + static int cmp(const LEX_CSTRING &a, const LEX_CSTRING &b) + { + DBUG_ASSERT(a.length == binary_length()); + DBUG_ASSERT(b.length == binary_length()); + return memcmp(a.str, b.str, b.length); + } + + /* + Convert from the in-memory to the in-record representation. + Used in Field::store_native(). + */ + static void memory_to_record(char *to, const char *from) + { + memcpy(to, from, NATIVE_LEN); + } + /* + Convert from the in-record to the in-memory representation + Used in Field::val_native(). + */ + static void record_to_memory(char *to, const char *from) + { + memcpy(to, from, NATIVE_LEN); + } + + /* + Hash the in-record representation + Used in Field::hash(). + */ + static void hash_record(const uchar *ptr, ulong *nr, ulong *nr2) + { + my_charset_bin.hash_sort(ptr, binary_length(), nr, nr2); + } + + static bool only_zero_bytes(const char *ptr, size_t length) + { + for (uint i= 0 ; i < length; i++) + { + if (ptr[i] != 0) + return false; + } + return true; + } + + static ulong KEY_pack_flags(uint column_nr) + { + /* + Return zero by default. A particular data type can override + this method return some flags, e.g. HA_PACK_KEY to enable + key prefix compression. + */ + return 0; + } + + /* + Convert from the in-record to the binlog representation. + Used in Field::pack(), and in filesort to store the addon fields. + By default, do what BINARY(N) does. + */ + static uchar *pack(uchar *to, const uchar *from, uint max_length) + { + return StringPack(&my_charset_bin, binary_length()).pack(to, from, max_length); + } + + /* + Convert from the in-binary-log to the in-record representation. + Used in Field::unpack(). + By default, do what BINARY(N) does. + */ + static const uchar *unpack(uchar *to, const uchar *from, const uchar *from_end, + uint param_data) + { + return StringPack(&my_charset_bin, binary_length()).unpack(to, from, from_end, + param_data); + } + +}; +#endif /* SQL_TYPE_FIXEDBIN_STORAGE */ diff --git a/sql/sql_update.cc b/sql/sql_update.cc index f5aec151686..539bea958e8 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -271,7 +271,7 @@ static void prepare_record_for_error_message(int error, TABLE *table) DBUG_VOID_RETURN; /* Create unique_map with all fields used by that index. */ - my_bitmap_init(&unique_map, unique_map_buf, table->s->fields, FALSE); + my_bitmap_init(&unique_map, unique_map_buf, table->s->fields); table->mark_index_columns(keynr, &unique_map); /* Subtract read_set and write_set. */ @@ -1002,6 +1002,7 @@ update_begin: THD_STAGE_INFO(thd, stage_updating); fix_rownum_pointers(thd, thd->lex->current_select, &updated_or_same); + thd->get_stmt_da()->reset_current_row_for_warning(1); while (!(error=info.read_record()) && !thd->killed) { explain->tracker.on_record_read(); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 34814d4afae..8ebefbb3d82 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -782,6 +782,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %token <kwd> CATALOG_NAME_SYM /* SQL-2003-N */ %token <kwd> CHAIN_SYM /* SQL-2003-N */ %token <kwd> CHANGED +%token <kwd> CHANNEL_SYM %token <kwd> CHARSET %token <kwd> CHECKPOINT_SYM %token <kwd> CHECKSUM_SYM @@ -1463,7 +1464,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); field_options last_field_options %type <ulonglong_number> - ulonglong_num real_ulonglong_num size_number + ulonglong_num real_ulonglong_num %type <longlong_number> longlong_num @@ -2080,6 +2081,7 @@ change: Lex->sql_command = SQLCOM_CHANGE_MASTER; } master_defs + optional_for_channel {} ; @@ -2311,6 +2313,34 @@ connection_name: } ; +optional_for_channel: + /* empty */ + { + /*do nothing */ + } + | for_channel + + ; + +for_channel: + FOR_SYM CHANNEL_SYM TEXT_STRING_sys + { + if (Lex->mi.connection_name.str != NULL) + { + my_yyabort_error((ER_WRONG_ARGUMENTS, MYF(0), "CONNECTION_NAME AND FOR CHANNEL CAN NOT BE SPECIFIED AT THE SAME TIME)")); + } + else + { + Lex->mi.connection_name= $3; +#ifdef HAVE_REPLICATION + if (unlikely(check_master_connection_name(&$3))) + my_yyabort_error((ER_WRONG_ARGUMENTS, MYF(0), "MASTER_CONNECTION_NAME")); +#endif + } + + } + ; + /* create a table */ create: @@ -2552,14 +2582,6 @@ create: $1 | $3))) MYSQL_YYABORT; } - | CREATE LOGFILE_SYM GROUP_SYM logfile_group_info - { - Lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP; - } - | CREATE TABLESPACE tablespace_info - { - Lex->alter_tablespace_info->ts_cmd_type= CREATE_TABLESPACE; - } | create_or_replace { Lex->set_command(SQLCOM_CREATE_SERVER, $1); } server_def { } @@ -3453,6 +3475,8 @@ signal_condition_information_item_name: { $$= DIAG_MESSAGE_TEXT; } | MYSQL_ERRNO_SYM { $$= DIAG_MYSQL_ERRNO; } + | ROW_NUMBER_SYM + { $$= DIAG_ROW_NUMBER; } ; resignal_stmt: @@ -3533,6 +3557,11 @@ simple_target_specification: } | '@' ident_or_text { + if (!$2.length) + { + thd->parse_error(); + YYABORT; + } $$= new (thd->mem_root) Item_func_get_user_var(thd, &$2); if (unlikely($$ == NULL)) MYSQL_YYABORT; @@ -3609,6 +3638,8 @@ condition_information_item_name: { $$= Condition_information_item::MYSQL_ERRNO; } | RETURNED_SQLSTATE_SYM { $$= Condition_information_item::RETURNED_SQLSTATE; } + | ROW_NUMBER_SYM + { $$= Condition_information_item::ROW_NUMBER; } ; sp_decl_ident: @@ -4308,350 +4339,6 @@ trg_event: | DELETE_SYM { Lex->trg_chistics.event= TRG_EVENT_DELETE; } ; -/* - This part of the parser contains common code for all TABLESPACE - commands. - CREATE TABLESPACE name ... - ALTER TABLESPACE name CHANGE DATAFILE ... - ALTER TABLESPACE name ADD DATAFILE ... - ALTER TABLESPACE name access_mode - CREATE LOGFILE GROUP_SYM name ... - ALTER LOGFILE GROUP_SYM name ADD UNDOFILE .. - ALTER LOGFILE GROUP_SYM name ADD REDOFILE .. - DROP TABLESPACE name - DROP LOGFILE GROUP_SYM name -*/ -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 - { - 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; - } - | tablespace_name - DROP ts_datafile - 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 - ; - -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_option: - opt_ts_initial_size - | opt_ts_autoextend_size - | opt_ts_max_size - ; - -tablespace_option_list: - tablespace_options - ; - -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 - ; - -alter_tablespace_option_list: - alter_tablespace_options - ; - -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 - ; - -logfile_group_option_list: - logfile_group_options - ; - -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 - ; - -alter_logfile_group_option_list: - 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_option: - opt_ts_initial_size - | opt_ts_engine - | ts_wait - ; - - -ts_datafile: - DATAFILE_SYM TEXT_STRING_sys - { - 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 - { - LEX *lex= Lex; - lex->alter_tablespace_info= (new (thd->mem_root) - st_alter_tablespace()); - if (unlikely(lex->alter_tablespace_info == NULL)) - MYSQL_YYABORT; - lex->alter_tablespace_info->tablespace_name= $1.str; - lex->sql_command= SQLCOM_ALTER_TABLESPACE; - } - ; - -logfile_group_name: - ident - { - LEX *lex= Lex; - lex->alter_tablespace_info= (new (thd->mem_root) - st_alter_tablespace()); - if (unlikely(lex->alter_tablespace_info == NULL)) - MYSQL_YYABORT; - lex->alter_tablespace_info->logfile_group_name= $1.str; - lex->sql_command= SQLCOM_ALTER_TABLESPACE; - } - ; - -ts_access_mode: - READ_ONLY_SYM - { - LEX *lex= Lex; - lex->alter_tablespace_info->ts_access_mode= TS_READ_ONLY; - } - | READ_WRITE_SYM - { - LEX *lex= Lex; - lex->alter_tablespace_info->ts_access_mode= TS_READ_WRITE; - } - | 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 - { - LEX *lex= Lex; - if (unlikely(lex->alter_tablespace_info->nodegroup_id != UNDEF_NODEGROUP)) - my_yyabort_error((ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"NODEGROUP")); - lex->alter_tablespace_info->nodegroup_id= $3; - } - ; - -opt_ts_comment: - COMMENT_SYM opt_equal TEXT_STRING_sys - { - LEX *lex= Lex; - if (unlikely(lex->alter_tablespace_info->ts_comment != NULL)) - my_yyabort_error((ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"COMMENT")); - lex->alter_tablespace_info->ts_comment= $3.str; - } - ; - -opt_ts_engine: - opt_storage ENGINE_SYM opt_equal storage_engines - { - LEX *lex= Lex; - if (unlikely(lex->alter_tablespace_info->storage_engine != NULL)) - my_yyabort_error((ER_FILEGROUP_OPTION_ONLY_ONCE, MYF(0), - "STORAGE ENGINE")); - lex->alter_tablespace_info->storage_engine= $4; - } - ; - -opt_ts_wait: - /* empty */ - | ts_wait - ; - -ts_wait: - WAIT_SYM - { - LEX *lex= Lex; - lex->alter_tablespace_info->wait_until_completed= TRUE; - } - | NO_WAIT_SYM - { - LEX *lex= Lex; - if (unlikely(!(lex->alter_tablespace_info->wait_until_completed))) - my_yyabort_error((ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"NO_WAIT")); - lex->alter_tablespace_info->wait_until_completed= FALSE; - } - ; - -size_number: - real_ulonglong_num { $$= $1;} - | IDENT_sys - { - if ($1.to_size_number(&$$)) - MYSQL_YYABORT; - } - ; - -/* - End tablespace part -*/ create_body: create_field_list_parens @@ -5025,8 +4712,13 @@ part_def_list: | part_def_list ',' part_definition {} ; +opt_partition: + /* empty */ + | PARTITION_SYM + ; + part_definition: - PARTITION_SYM + opt_partition { partition_info *part_info= Lex->part_info; partition_element *p_elem= new (thd->mem_root) partition_element(); @@ -5372,7 +5064,7 @@ opt_part_option_list: opt_part_option: TABLESPACE opt_equal ident_or_text - { Lex->part_info->curr_part_elem->tablespace_name= $3.str; } + { /* Compatibility with MySQL */ } | opt_storage ENGINE_SYM opt_equal storage_engines { partition_info *part_info= Lex->part_info; @@ -5711,7 +5403,7 @@ create_table_option: Lex->create_info.used_fields|= HA_CREATE_USED_INDEXDIR; } | TABLESPACE ident - {Lex->create_info.tablespace= $2.str;} + { /* Compatiblity with MySQL */ } | STORAGE_SYM DISK_SYM {Lex->create_info.storage_media= HA_SM_DISK;} | STORAGE_SYM MEMORY_SYM @@ -5780,7 +5472,7 @@ versioning_option: { if (unlikely(Lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) { - if (DBUG_EVALUATE_IF("sysvers_force", 0, 1)) + if (!DBUG_IF("sysvers_force")) { my_error(ER_VERS_NOT_SUPPORTED, MYF(0), "CREATE TEMPORARY TABLE"); MYSQL_YYABORT; @@ -7360,26 +7052,6 @@ alter: Lex->pop_select(); //main select } - | ALTER TABLESPACE alter_tablespace_info - { - LEX *lex= Lex; - lex->alter_tablespace_info->ts_cmd_type= ALTER_TABLESPACE; - } - | ALTER LOGFILE_SYM GROUP_SYM alter_logfile_group_info - { - LEX *lex= Lex; - lex->alter_tablespace_info->ts_cmd_type= ALTER_LOGFILE_GROUP; - } - | ALTER TABLESPACE change_tablespace_info - { - LEX *lex= Lex; - lex->alter_tablespace_info->ts_cmd_type= CHANGE_FILE_TABLESPACE; - } - | ALTER TABLESPACE change_tablespace_access - { - LEX *lex= Lex; - lex->alter_tablespace_info->ts_cmd_type= ALTER_ACCESS_MODE_TABLESPACE; - } | ALTER SERVER_SYM ident_or_text { LEX *lex= Lex; @@ -7613,6 +7285,54 @@ alter_commands: if (Lex->stmt_alter_table_exchange_partition($6)) MYSQL_YYABORT; } + | CONVERT_SYM PARTITION_SYM alt_part_name_item + TO_SYM TABLE_SYM table_ident have_partitioning + { + LEX *lex= Lex; + if (Lex->stmt_alter_table($6)) + MYSQL_YYABORT; + lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_table(); + if (unlikely(lex->m_sql_cmd == NULL)) + MYSQL_YYABORT; + lex->alter_info.partition_flags|= ALTER_PARTITION_CONVERT_OUT; + } + | CONVERT_SYM TABLE_SYM table_ident + { + LEX *lex= Lex; + if (!lex->first_select_lex()->add_table_to_list(thd, $3, nullptr, 0, + TL_READ_NO_INSERT, + MDL_SHARED_NO_WRITE)) + MYSQL_YYABORT; + + /* + This will appear as (new_db, new_name) in alter_ctx. + new_db will be IX-locked and new_name X-locked. + */ + lex->first_select_lex()->db= $3->db; + lex->name= $3->table; + if (lex->first_select_lex()->db.str == NULL && + lex->copy_db_to(&lex->first_select_lex()->db)) + MYSQL_YYABORT; + + lex->part_info= new (thd->mem_root) partition_info(); + if (unlikely(!lex->part_info)) + MYSQL_YYABORT; + + lex->part_info->num_parts= 1; + /* + OR-ed with ALTER_PARTITION_ADD because too many checks of + ALTER_PARTITION_ADD required. + */ + lex->alter_info.partition_flags|= ALTER_PARTITION_ADD | + ALTER_PARTITION_CONVERT_IN; + } + TO_SYM PARTITION_SYM part_definition + { + LEX *lex= Lex; + lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_table(); + if (unlikely(lex->m_sql_cmd == NULL)) + MYSQL_YYABORT; + } ; remove_partitioning: @@ -7861,17 +7581,9 @@ alter_list_item: } | RENAME opt_to table_ident { - LEX *lex=Lex; - lex->first_select_lex()->db= $3->db; - if (lex->first_select_lex()->db.str == NULL && - lex->copy_db_to(&lex->first_select_lex()->db)) + if (Lex->stmt_alter_table($3)) MYSQL_YYABORT; - if (unlikely(check_table_name($3->table.str,$3->table.length, - FALSE)) || - ($3->db.str && unlikely(check_db_name((LEX_STRING*) &$3->db)))) - my_yyabort_error((ER_WRONG_TABLE_NAME, MYF(0), $3->table.str)); - lex->name= $3->table; - lex->alter_info.flags|= ALTER_RENAME; + Lex->alter_info.flags|= ALTER_RENAME; } | RENAME COLUMN_SYM opt_if_exists_table_element ident TO_SYM ident { @@ -8038,7 +7750,7 @@ opt_to: ; slave: - START_SYM SLAVE optional_connection_name slave_thread_opts + START_SYM SLAVE optional_connection_name slave_thread_opts optional_for_channel { LEX *lex=Lex; lex->sql_command = SQLCOM_SLAVE_START; @@ -8055,7 +7767,7 @@ slave: /* If you change this code don't forget to update STOP SLAVE too */ } {} - | STOP_SYM SLAVE optional_connection_name slave_thread_opts + | STOP_SYM SLAVE optional_connection_name slave_thread_opts optional_for_channel { LEX *lex=Lex; lex->sql_command = SQLCOM_SLAVE_STOP; @@ -11278,6 +10990,11 @@ variable_aux: ident_or_text SET_VAR expr { Item_func_set_user_var *item; + if (!$1.length) + { + thd->parse_error(); + YYABORT; + } $$= item= new (thd->mem_root) Item_func_set_user_var(thd, &$1, $3); if (unlikely($$ == NULL)) MYSQL_YYABORT; @@ -11287,6 +11004,11 @@ variable_aux: } | ident_or_text { + if (!$1.length) + { + thd->parse_error(); + YYABORT; + } $$= new (thd->mem_root) Item_func_get_user_var(thd, &$1); if (unlikely($$ == NULL)) MYSQL_YYABORT; @@ -12928,6 +12650,12 @@ select_var_ident: select_outvar select_outvar: '@' ident_or_text { + if (!$2.length) + { + thd->parse_error(); + YYABORT; + } + $$ = Lex->result ? new (thd->mem_root) my_var_user(&$2) : NULL; } | ident_or_text @@ -13076,16 +12804,6 @@ drop: lex->set_command(SQLCOM_DROP_TRIGGER, $3); lex->spname= $4; } - | DROP TABLESPACE tablespace_name opt_ts_engine opt_ts_wait - { - LEX *lex= Lex; - lex->alter_tablespace_info->ts_cmd_type= DROP_TABLESPACE; - } - | DROP LOGFILE_SYM GROUP_SYM logfile_group_name opt_ts_engine opt_ts_wait - { - LEX *lex= Lex; - lex->alter_tablespace_info->ts_cmd_type= DROP_LOGFILE_GROUP; - } | DROP SERVER_SYM opt_if_exists ident_or_text { Lex->set_command(SQLCOM_DROP_SERVER, $3); @@ -13190,6 +12908,8 @@ insert: { Lex->sql_command= SQLCOM_INSERT; Lex->duplicates= DUP_ERROR; + thd->get_stmt_da()->opt_clear_warning_info(thd->query_id); + thd->get_stmt_da()->reset_current_row_for_warning(1); } insert_start insert_lock_option opt_ignore opt_into insert_table { @@ -13199,6 +12919,7 @@ insert: stmt_end { Lex->mark_first_table_as_inserting(); + thd->get_stmt_da()->reset_current_row_for_warning(0); } ; @@ -13207,6 +12928,8 @@ replace: { Lex->sql_command = SQLCOM_REPLACE; Lex->duplicates= DUP_REPLACE; + thd->get_stmt_da()->opt_clear_warning_info(thd->query_id); + thd->get_stmt_da()->reset_current_row_for_warning(1); } insert_start replace_lock_option opt_into insert_table { @@ -13216,6 +12939,7 @@ replace: stmt_end { Lex->mark_first_table_as_inserting(); + thd->get_stmt_da()->reset_current_row_for_warning(0); } ; @@ -13366,6 +13090,7 @@ no_braces: opt_values ')' { LEX *lex=Lex; + thd->get_stmt_da()->inc_current_row_for_warning(); if (unlikely(lex->many_values.push_back(lex->insert_list, thd->mem_root))) MYSQL_YYABORT; @@ -13381,6 +13106,7 @@ no_braces_with_names: opt_values_with_names ')' { LEX *lex=Lex; + thd->get_stmt_da()->inc_current_row_for_warning(); if (unlikely(lex->many_values.push_back(lex->insert_list, thd->mem_root))) MYSQL_YYABORT; @@ -13942,7 +13668,8 @@ show_param: LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_RELAYLOG_EVENTS; } - opt_global_limit_clause + opt_global_limit_clause optional_for_channel + { } | keys_or_index from_or_in table_ident opt_db opt_where_clause { LEX *lex= Lex; @@ -14087,16 +13814,7 @@ show_param: MYSQL_YYABORT; Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; } - | SLAVE STATUS_SYM - { - LEX *lex= thd->lex; - lex->mi.connection_name= null_clex_str; - if (!(lex->m_sql_cmd= new (thd->mem_root) - Sql_cmd_show_slave_status())) - MYSQL_YYABORT; - lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; - } - | SLAVE connection_name STATUS_SYM + | SLAVE optional_connection_name STATUS_SYM optional_for_channel { if (!(Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_show_slave_status())) @@ -14481,7 +14199,7 @@ flush_option: { Lex->type|= REFRESH_SLOW_LOG; } | BINARY LOGS_SYM opt_delete_gtid_domain { Lex->type|= REFRESH_BINARY_LOG; } - | RELAY LOGS_SYM optional_connection_name + | RELAY LOGS_SYM optional_connection_name optional_for_channel { LEX *lex= Lex; if (unlikely(lex->type & REFRESH_RELAY_LOG)) @@ -14632,7 +14350,8 @@ reset_options: reset_option: SLAVE { Lex->type|= REFRESH_SLAVE; } optional_connection_name - slave_reset_options { } + slave_reset_options optional_for_channel + { } | MASTER_SYM { Lex->type|= REFRESH_MASTER; @@ -14922,6 +14641,12 @@ field_or_var: simple_ident_nospvar {$$= $1;} | '@' ident_or_text { + if (!$2.length) + { + thd->parse_error(); + YYABORT; + } + $$= new (thd->mem_root) Item_user_var_as_out_param(thd, &$2); if (unlikely($$ == NULL)) MYSQL_YYABORT; @@ -15929,6 +15654,7 @@ keyword_sp_var_and_label: | CASCADED | CATALOG_NAME_SYM | CHAIN_SYM + | CHANNEL_SYM | CHANGED | CIPHER_SYM | CLIENT_SYM @@ -16736,6 +16462,12 @@ option_value_no_option_type: } | '@' ident_or_text equal { + if (!$2.length) + { + thd->parse_error(); + YYABORT; + } + if (sp_create_assignment_lex(thd, $1.str)) MYSQL_YYABORT; } diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index aa2ec70e3b3..7127cbc00f6 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -740,6 +740,23 @@ static Sys_var_charptr_fscs Sys_character_sets_dir( READ_ONLY GLOBAL_VAR(charsets_dir), CMD_LINE(REQUIRED_ARG), DEFAULT(0)); +static bool check_engine_supports_temporary(sys_var *self, THD *thd, set_var *var) +{ + plugin_ref plugin= var->save_result.plugin; + if (!plugin) + return false; + DBUG_ASSERT(plugin); + handlerton *hton= plugin_hton(plugin); + DBUG_ASSERT(hton); + if (ha_check_storage_engine_flag(hton, HTON_TEMPORARY_NOT_SUPPORTED)) + { + my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), hton_name(hton)->str, + "TEMPORARY"); + return true; + } + return false; +} + static bool check_not_null(sys_var *self, THD *thd, set_var *var) { return var->value && var->value->is_null(); @@ -4245,7 +4262,8 @@ static Sys_var_plugin Sys_storage_engine( static Sys_var_plugin Sys_default_tmp_storage_engine( "default_tmp_storage_engine", "The default storage engine for user-created temporary tables", SESSION_VAR(tmp_table_plugin), NO_CMD_LINE, - MYSQL_STORAGE_ENGINE_PLUGIN, DEFAULT(&default_tmp_storage_engine)); + MYSQL_STORAGE_ENGINE_PLUGIN, DEFAULT(&default_tmp_storage_engine), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_engine_supports_temporary)); static Sys_var_plugin Sys_enforce_storage_engine( "enforce_storage_engine", "Force the use of a storage engine for new tables", @@ -6015,16 +6033,6 @@ static Sys_var_mybool Sys_wsrep_desync ( ON_CHECK(wsrep_desync_check), ON_UPDATE(wsrep_desync_update)); -static Sys_var_mybool Sys_wsrep_strict_ddl ( - "wsrep_strict_ddl", - "If set, reject DDL on affected tables not supporting Galera replication", - GLOBAL_VAR(wsrep_strict_ddl), - CMD_LINE(OPT_ARG), DEFAULT(FALSE), - NO_MUTEX_GUARD, NOT_IN_BINLOG, - ON_CHECK(0), - ON_UPDATE(wsrep_strict_ddl_update), - DEPRECATED("'@@wsrep_mode=STRICT_REPLICATION'")); // since 10.6.0 - static const char *wsrep_reject_queries_names[]= { "NONE", "ALL", "ALL_KILL", NullS }; static Sys_var_enum Sys_wsrep_reject_queries( "wsrep_reject_queries", "Variable to set to reject queries", @@ -6045,13 +6053,6 @@ static Sys_var_mybool Sys_wsrep_recover_datadir( READ_ONLY GLOBAL_VAR(wsrep_recovery), CMD_LINE(OPT_ARG), DEFAULT(FALSE)); -static Sys_var_mybool Sys_wsrep_replicate_myisam( - "wsrep_replicate_myisam", "To enable myisam replication", - GLOBAL_VAR(wsrep_replicate_myisam), CMD_LINE(OPT_ARG), DEFAULT(FALSE), - NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), - ON_UPDATE(wsrep_replicate_myisam_update), - DEPRECATED("'@@wsrep_mode=REPLICATE_MYISAM'")); // since 10.6.0 - static Sys_var_mybool Sys_wsrep_log_conflicts( "wsrep_log_conflicts", "To log multi-master conflicts", GLOBAL_VAR(wsrep_log_conflicts), CMD_LINE(OPT_ARG), DEFAULT(FALSE)); diff --git a/sql/table.cc b/sql/table.cc index 5a622f9dbfa..b1a7b6bfe2b 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -3337,7 +3337,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, share->column_bitmap_size * bitmap_count))) goto err; - my_bitmap_init(&share->all_set, bitmaps, share->fields, FALSE); + my_bitmap_init(&share->all_set, bitmaps, share->fields); bitmap_set_all(&share->all_set); if (share->check_set) { @@ -3348,7 +3348,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, my_bitmap_init(share->check_set, (my_bitmap_map*) ((uchar*) bitmaps + share->column_bitmap_size), - share->fields, FALSE); + share->fields); bitmap_clear_all(share->check_set); } @@ -4300,26 +4300,26 @@ partititon_err: goto err; my_bitmap_init(&outparam->def_read_set, - (my_bitmap_map*) bitmaps, share->fields, FALSE); + (my_bitmap_map*) bitmaps, share->fields); bitmaps+= bitmap_size; my_bitmap_init(&outparam->def_write_set, - (my_bitmap_map*) bitmaps, share->fields, FALSE); + (my_bitmap_map*) bitmaps, share->fields); bitmaps+= bitmap_size; my_bitmap_init(&outparam->has_value_set, - (my_bitmap_map*) bitmaps, share->fields, FALSE); + (my_bitmap_map*) bitmaps, share->fields); bitmaps+= bitmap_size; my_bitmap_init(&outparam->tmp_set, - (my_bitmap_map*) bitmaps, share->fields, FALSE); + (my_bitmap_map*) bitmaps, share->fields); bitmaps+= bitmap_size; my_bitmap_init(&outparam->eq_join_set, - (my_bitmap_map*) bitmaps, share->fields, FALSE); + (my_bitmap_map*) bitmaps, share->fields); bitmaps+= bitmap_size; my_bitmap_init(&outparam->cond_set, - (my_bitmap_map*) bitmaps, share->fields, FALSE); + (my_bitmap_map*) bitmaps, share->fields); bitmaps+= bitmap_size; my_bitmap_init(&outparam->def_rpl_write_set, - (my_bitmap_map*) bitmaps, share->fields, FALSE); + (my_bitmap_map*) bitmaps, share->fields); outparam->default_column_bitmaps(); outparam->cond_selectivity= 1.0; @@ -9716,6 +9716,9 @@ bool TR_table::update(ulonglong start_id, ulonglong end_id) int error= table->file->ha_write_row(table->record[0]); if (unlikely(error)) table->file->print_error(error, MYF(0)); + /* extra() is used to apply the bulk insert operation + on mysql/transaction_registry table */ + table->file->extra(HA_EXTRA_IGNORE_INSERT); return error; } diff --git a/sql/thread_pool_info.cc b/sql/thread_pool_info.cc index 90ac6871784..e3ffd160a11 100644 --- a/sql/thread_pool_info.cc +++ b/sql/thread_pool_info.cc @@ -14,9 +14,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 - 1301 USA*/ #include <mysql_version.h> -#include <mysql/plugin.h> #include <my_global.h> +#include <mysql/plugin.h> #include <sql_class.h> #include <sql_i_s.h> #include <mysql/plugin.h> diff --git a/sql/tztime.cc b/sql/tztime.cc index 0e334bec3dd..1f393e24ec2 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -2524,7 +2524,7 @@ scan_tz_dir(char * name_end, uint symlink_recursion_level, uint verbose) { MY_DIR *cur_dir; char *name_end_tmp; - uint i; + size_t i; /* Sort directory data, to pass mtr tests on different platforms. */ if (!(cur_dir= my_dir(fullname, MYF(MY_WANT_STAT|MY_WANT_SORT)))) diff --git a/sql/uniques.cc b/sql/uniques.cc index a0cebe3e4dd..572d80f0b64 100644 --- a/sql/uniques.cc +++ b/sql/uniques.cc @@ -709,7 +709,7 @@ bool Unique::merge(TABLE *table, uchar *buff, size_t buff_size, { IO_CACHE *outfile= &sort.io_cache; Merge_chunk *file_ptr= (Merge_chunk*) file_ptrs.buffer; - uint maxbuffer= file_ptrs.elements - 1; + uint maxbuffer= (uint)file_ptrs.elements - 1; my_off_t save_pos; bool error= 1; Sort_param sort_param; diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 06cd65dd01a..641e203d442 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -90,7 +90,6 @@ my_bool wsrep_drupal_282555_workaround; // Retry autoinc insert after du my_bool wsrep_certify_nonPK; // Certify, even when no primary key ulong wsrep_certification_rules = WSREP_CERTIFICATION_RULES_STRICT; my_bool wsrep_recovery; // Recovery -my_bool wsrep_replicate_myisam; // Enable MyISAM replication my_bool wsrep_log_conflicts; my_bool wsrep_load_data_splitting= 0; // Commit load data every 10K intervals my_bool wsrep_slave_UK_checks; // Slave thread does UK checks @@ -100,9 +99,6 @@ my_bool wsrep_restart_slave; // Should mysql slave thread be my_bool wsrep_desync; // De(re)synchronize the node from the // cluster ulonglong wsrep_mode; -my_bool wsrep_strict_ddl; // Deprecated: Reject DDL to - // effected tables not - // supporting Galera replication bool wsrep_service_started; // If Galera was initialized long wsrep_slave_threads; // No. of slave appliers threads ulong wsrep_retry_autocommit; // Retry aborted autocommit trx @@ -1607,11 +1603,18 @@ wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* tables, wsrep::key_array* key { bool fail= false; TABLE_LIST *table; + TABLE_LIST *table_last_in_list; thd->release_transactional_locks(); uint counter; MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); + for (table_last_in_list= tables;;table_last_in_list= table_last_in_list->next_local) { + if (!table_last_in_list->next_local) { + break; + } + } + if (thd->open_temporary_tables(tables) || open_tables(thd, &tables, &counter, MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL)) { @@ -1643,11 +1646,19 @@ exit: /* close the table and release MDL locks */ close_thread_tables(thd); thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + bool invalidate_next_global= false; for (table= tables; table; table= table->next_local) { table->table= NULL; - table->next_global= NULL; table->mdl_request.ticket= NULL; + // We should invalidate `next_global` only for entries that are added + // in this function + if (table == table_last_in_list) { + invalidate_next_global= true; + } + if (invalidate_next_global) { + table->next_global= NULL; + } } return fail; @@ -2556,6 +2567,8 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, } thd_proc_info(thd, "acquiring total order isolation"); + WSREP_DEBUG("wsrep_TOI_begin for %s", wsrep_thd_query(thd)); + THD_STAGE_INFO(thd, stage_waiting_isolation); wsrep::client_state& cs(thd->wsrep_cs()); @@ -2898,39 +2911,49 @@ void wsrep_handle_mdl_conflict(MDL_context *requestor_ctx, const MDL_ticket *ticket, const MDL_key *key) { - /* Fallback to the non-wsrep behaviour */ - if (!WSREP_ON) return; - THD *request_thd= requestor_ctx->get_thd(); THD *granted_thd= ticket->get_ctx()->get_thd(); + /* Fallback to the non-wsrep behaviour */ + if (!WSREP(request_thd)) return; + const char* schema= key->db_name(); int schema_len= key->db_name_length(); mysql_mutex_lock(&request_thd->LOCK_thd_data); - if (wsrep_thd_is_toi(request_thd) || - wsrep_thd_is_applying(request_thd)) { + if (wsrep_thd_is_toi(request_thd) || + wsrep_thd_is_applying(request_thd)) + { + WSREP_DEBUG("wsrep_handle_mdl_conflict request TOI/APPLY for %s", + wsrep_thd_query(request_thd)); + THD_STAGE_INFO(request_thd, stage_waiting_isolation); mysql_mutex_unlock(&request_thd->LOCK_thd_data); WSREP_MDL_LOG(DEBUG, "MDL conflict ", schema, schema_len, request_thd, granted_thd); ticket->wsrep_report(wsrep_debug); mysql_mutex_lock(&granted_thd->LOCK_thd_data); + if (wsrep_thd_is_toi(granted_thd) || wsrep_thd_is_applying(granted_thd)) { if (wsrep_thd_is_aborting(granted_thd)) { - WSREP_DEBUG("BF thread waiting for SR in aborting state"); + WSREP_DEBUG("BF thread waiting for SR in aborting state for %s", + wsrep_thd_query(request_thd)); + THD_STAGE_INFO(request_thd, stage_waiting_isolation); ticket->wsrep_report(wsrep_debug); mysql_mutex_unlock(&granted_thd->LOCK_thd_data); } else if (wsrep_thd_is_SR(granted_thd) && !wsrep_thd_is_SR(request_thd)) { - WSREP_MDL_LOG(INFO, "MDL conflict, DDL vs SR", + WSREP_MDL_LOG(INFO, "MDL conflict, DDL vs SR", schema, schema_len, request_thd, granted_thd); mysql_mutex_unlock(&granted_thd->LOCK_thd_data); + WSREP_DEBUG("wsrep_handle_mdl_conflict DDL vs SR for %s", + wsrep_thd_query(request_thd)); + THD_STAGE_INFO(request_thd, stage_waiting_isolation); wsrep_abort_thd(request_thd, granted_thd, 1); } else @@ -2945,14 +2968,18 @@ void wsrep_handle_mdl_conflict(MDL_context *requestor_ctx, else if (granted_thd->lex->sql_command == SQLCOM_FLUSH || granted_thd->mdl_context.has_explicit_locks()) { - WSREP_DEBUG("BF thread waiting for FLUSH"); + WSREP_DEBUG("BF thread waiting for FLUSH for %s", + wsrep_thd_query(request_thd)); + THD_STAGE_INFO(request_thd, stage_waiting_ddl); ticket->wsrep_report(wsrep_debug); mysql_mutex_unlock(&granted_thd->LOCK_thd_data); } else if (request_thd->lex->sql_command == SQLCOM_DROP_TABLE) { - WSREP_DEBUG("DROP caused BF abort, conf %s", - wsrep_thd_transaction_state_str(granted_thd)); + WSREP_DEBUG("DROP caused BF abort, conf %s for %s", + wsrep_thd_transaction_state_str(granted_thd), + wsrep_thd_query(request_thd)); + THD_STAGE_INFO(request_thd, stage_waiting_isolation); ticket->wsrep_report(wsrep_debug); mysql_mutex_unlock(&granted_thd->LOCK_thd_data); wsrep_abort_thd(request_thd, granted_thd, 1); @@ -2961,7 +2988,11 @@ void wsrep_handle_mdl_conflict(MDL_context *requestor_ctx, { WSREP_MDL_LOG(DEBUG, "MDL conflict-> BF abort", schema, schema_len, request_thd, granted_thd); + WSREP_DEBUG("wsrep_handle_mdl_conflict -> BF abort for %s", + wsrep_thd_query(request_thd)); + THD_STAGE_INFO(request_thd, stage_waiting_isolation); ticket->wsrep_report(wsrep_debug); + if (granted_thd->wsrep_trx().active()) { mysql_mutex_unlock(&granted_thd->LOCK_thd_data); @@ -2974,14 +3005,16 @@ void wsrep_handle_mdl_conflict(MDL_context *requestor_ctx, thd is BF, BF abort and wait. */ mysql_mutex_unlock(&granted_thd->LOCK_thd_data); + if (wsrep_thd_is_BF(request_thd, FALSE)) { ha_abort_transaction(request_thd, granted_thd, TRUE); } else { - WSREP_MDL_LOG(INFO, "MDL unknown BF-BF conflict", schema, schema_len, - request_thd, granted_thd); + WSREP_MDL_LOG(INFO, "MDL unknown BF-BF conflict", + schema, schema_len, + request_thd, granted_thd); ticket->wsrep_report(true); unireg_abort(1); } diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index 1d72f884966..9cbe186aedf 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -85,7 +85,6 @@ extern ulong wsrep_forced_binlog_format; extern my_bool wsrep_desync; extern ulong wsrep_reject_queries; extern my_bool wsrep_recovery; -extern my_bool wsrep_replicate_myisam; extern my_bool wsrep_log_conflicts; extern ulong wsrep_mysql_replication_bundle; extern my_bool wsrep_load_data_splitting; @@ -103,7 +102,6 @@ extern bool wsrep_new_cluster; extern bool wsrep_gtid_mode; extern uint wsrep_gtid_domain_id; extern ulonglong wsrep_mode; -extern my_bool wsrep_strict_ddl; enum enum_wsrep_reject_types { WSREP_REJECT_NONE, /* nothing rejected */ diff --git a/sql/wsrep_trans_observer.h b/sql/wsrep_trans_observer.h index c4528b8ef2a..70759e381a5 100644 --- a/sql/wsrep_trans_observer.h +++ b/sql/wsrep_trans_observer.h @@ -1,4 +1,4 @@ -/* Copyright 2016-2019 Codership Oy <http://www.codership.com> +/* Copyright 2016-2021 Codership Oy <http://www.codership.com> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -272,12 +272,14 @@ static inline int wsrep_before_commit(THD* thd, bool all) WSREP_DEBUG("wsrep_before_commit: %d, %lld", wsrep_is_real(thd, all), (long long)wsrep_thd_trx_seqno(thd)); + THD_STAGE_INFO(thd, stage_waiting_certification); int ret= 0; DBUG_ASSERT(wsrep_run_commit_hook(thd, all)); + if ((ret= thd->wsrep_cs().before_commit()) == 0) { DBUG_ASSERT(!thd->wsrep_trx().ws_meta().gtid().is_undefined()); - if (!thd->variables.gtid_seq_no && + if (!thd->variables.gtid_seq_no && (thd->wsrep_trx().ws_meta().flags() & wsrep::provider::flag::commit)) { uint64 seqno= 0; diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc index 1790141b37d..178ec603e72 100644 --- a/sql/wsrep_var.cc +++ b/sql/wsrep_var.cc @@ -836,10 +836,11 @@ bool wsrep_desync_check (sys_var *self, THD* thd, set_var* var) return true; } } else { + THD_STAGE_INFO(thd, stage_waiting_flow); ret= Wsrep_server_state::instance().provider().resync(); if (ret != WSREP_OK) { WSREP_WARN ("SET resync failed %d for schema: %s, query: %s", ret, - thd->get_db(), thd->query()); + thd->get_db(), wsrep_thd_query(thd)); my_error (ER_CANNOT_USER, MYF(0), "'resync'", thd->query()); return true; } @@ -1100,24 +1101,3 @@ bool wsrep_gtid_domain_id_update(sys_var* self, THD *thd, enum_var_type) return false; } -bool wsrep_strict_ddl_update(sys_var *self, THD* thd, enum_var_type var_type) -{ - // In case user still sets wsrep_strict_ddl we set new - // option to wsrep_mode - if (wsrep_strict_ddl) - wsrep_mode|= WSREP_MODE_STRICT_REPLICATION; - else - wsrep_mode&= (~WSREP_MODE_STRICT_REPLICATION); - return false; -} - -bool wsrep_replicate_myisam_update(sys_var *self, THD* thd, enum_var_type var_type) -{ - // In case user still sets wsrep_replicate_myisam we set new - // option to wsrep_mode - if (wsrep_replicate_myisam) - wsrep_mode|= WSREP_MODE_REPLICATE_MYISAM; - else - wsrep_mode&= (~WSREP_MODE_REPLICATE_MYISAM); - return false; -} diff --git a/sql/wsrep_var.h b/sql/wsrep_var.h index 7908e873795..0f811d70928 100644 --- a/sql/wsrep_var.h +++ b/sql/wsrep_var.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2013 Codership Oy <info@codership.com> +/* Copyright (C) 2013-2021 Codership Oy <info@codership.com> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -109,8 +109,6 @@ extern bool wsrep_gtid_seq_no_check CHECK_ARGS; extern bool wsrep_gtid_domain_id_update UPDATE_ARGS; extern bool wsrep_mode_check CHECK_ARGS; -extern bool wsrep_strict_ddl_update UPDATE_ARGS; -extern bool wsrep_replicate_myisam_update UPDATE_ARGS; #else /* WITH_WSREP */ #define wsrep_provider_init(X) |