diff options
Diffstat (limited to 'sql')
73 files changed, 1857 insertions, 905 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 4be129ac9d0..f6ddfbfaf38 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -193,7 +193,7 @@ DTRACE_INSTRUMENT(sql) TARGET_LINK_LIBRARIES(sql mysys mysys_ssl dbug strings vio pcre2-8 tpool - ${LIBWRAP} ${LIBCRYPT} ${LIBDL} ${CMAKE_THREAD_LIBS_INIT} + ${LIBWRAP} ${LIBCRYPT} ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT} ${SSL_LIBRARIES} ${LIBSYSTEMD}) diff --git a/sql/backup.cc b/sql/backup.cc index 02570dfd30d..e89f9a108a7 100644 --- a/sql/backup.cc +++ b/sql/backup.cc @@ -378,7 +378,13 @@ bool backup_reset_alter_copy_lock(THD *thd) bool backup_lock(THD *thd, TABLE_LIST *table) { + /* We should leave the previous table unlocked in case of errors */ backup_unlock(thd); + if (thd->locked_tables_mode) + { + my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); + return 1; + } table->mdl_request.duration= MDL_EXPLICIT; if (thd->mdl_context.acquire_lock(&table->mdl_request, thd->variables.lock_wait_timeout)) diff --git a/sql/derived_handler.cc b/sql/derived_handler.cc index 76fd736de2b..f48b95cbf76 100644 --- a/sql/derived_handler.cc +++ b/sql/derived_handler.cc @@ -120,8 +120,6 @@ void derived_handler::set_derived(TABLE_LIST *tbl) table= tbl->table; unit= tbl->derived; select= unit->first_select(); - tmp_table_param= select->next_select() ? - ((select_unit *)(unit->result))->get_tmp_table_param() : - &select->join->tmp_table_param; + tmp_table_param= ((select_unit *)(unit->result))->get_tmp_table_param(); } diff --git a/sql/field.h b/sql/field.h index be4d279ce61..dfc02149f9d 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1151,8 +1151,9 @@ public: virtual void reset_fields() {} const uchar *ptr_in_record(const uchar *record) const { - my_ptrdiff_t l_offset= (my_ptrdiff_t) (record - table->record[0]); - return ptr + l_offset; + my_ptrdiff_t l_offset= (my_ptrdiff_t) (ptr - table->record[0]); + DBUG_ASSERT(l_offset >= 0 && table->s->rec_buff_length - l_offset > 0); + return record + l_offset; } virtual int set_default(); diff --git a/sql/filesort.cc b/sql/filesort.cc index 2a713ecf97b..0337325b544 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -2208,6 +2208,12 @@ sortlength(THD *thd, Sort_keys *sort_keys, bool *allow_packing_for_sortkeys) length=0; uint nullable_cols=0; + if (sort_keys->is_parameters_computed()) + { + *allow_packing_for_sortkeys= sort_keys->using_packed_sortkeys(); + return sort_keys->get_sort_length_with_memcmp_values(); + } + for (SORT_FIELD *sortorder= sort_keys->begin(); sortorder != sort_keys->end(); sortorder++) @@ -2218,12 +2224,11 @@ sortlength(THD *thd, Sort_keys *sort_keys, bool *allow_packing_for_sortkeys) { Field *field= sortorder->field; CHARSET_INFO *cs= sortorder->field->sort_charset(); - sortorder->set_length_and_original_length(thd, field->sort_length()); - - sortorder->suffix_length= sortorder->field->sort_suffix_length(); sortorder->type= field->is_packable() ? SORT_FIELD_ATTR::VARIABLE_SIZE : SORT_FIELD_ATTR::FIXED_SIZE; + sortorder->set_length_and_original_length(thd, field->sort_length()); + sortorder->suffix_length= sortorder->field->sort_suffix_length(); sortorder->cs= cs; if (use_strnxfrm((cs=sortorder->field->sort_charset()))) @@ -2242,11 +2247,11 @@ sortlength(THD *thd, Sort_keys *sort_keys, bool *allow_packing_for_sortkeys) } else { - sortorder->item->type_handler()->sort_length(thd, sortorder->item, - sortorder); sortorder->type= sortorder->item->type_handler()->is_packable() ? SORT_FIELD_ATTR::VARIABLE_SIZE : SORT_FIELD_ATTR::FIXED_SIZE; + sortorder->item->type_handler()->sort_length(thd, sortorder->item, + sortorder); sortorder->cs= sortorder->item->collation.collation; if (sortorder->is_variable_sized() && allow_packing_for_keys) { @@ -2259,8 +2264,11 @@ sortlength(THD *thd, Sort_keys *sort_keys, bool *allow_packing_for_sortkeys) if ((sortorder->maybe_null= sortorder->item->maybe_null)) nullable_cols++; // Place for NULL marker } - set_if_smaller(sortorder->length, thd->variables.max_sort_length); - set_if_smaller(sortorder->original_length, thd->variables.max_sort_length); + if (sortorder->is_variable_sized()) + { + set_if_smaller(sortorder->length, thd->variables.max_sort_length); + set_if_smaller(sortorder->original_length, thd->variables.max_sort_length); + } length+=sortorder->length; sort_keys->increment_size_of_packable_fields(sortorder->length_bytes); @@ -2269,6 +2277,8 @@ sortlength(THD *thd, Sort_keys *sort_keys, bool *allow_packing_for_sortkeys) // add bytes for nullable_cols sort_keys->increment_original_sort_length(nullable_cols); *allow_packing_for_sortkeys= allow_packing_for_keys; + sort_keys->set_sort_length_with_memcmp_values(length + nullable_cols); + sort_keys->set_parameters_computed(true); DBUG_PRINT("info",("sort_length: %d",length)); return length + nullable_cols; } @@ -2527,7 +2537,7 @@ void Sort_param::try_to_pack_sortkeys() return; const uint sz= Sort_keys::size_of_length_field; - uint sort_len= sort_keys->get_sort_length(); + uint sort_len= sort_keys->get_sort_length_with_original_values(); /* Heuristic introduced, skip packing sort keys if saving less than 128 bytes @@ -2759,7 +2769,8 @@ bool SORT_FIELD_ATTR::check_if_packing_possible(THD *thd) const void SORT_FIELD_ATTR::set_length_and_original_length(THD *thd, uint length_arg) { length= length_arg; - set_if_smaller(length, thd->variables.max_sort_length); + if (is_variable_sized()) + set_if_smaller(length, thd->variables.max_sort_length); original_length= length_arg; } diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index ece9d05c4fd..7f00a194698 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -2069,6 +2069,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, DBUG_ASSERT(part_elem->part_state == PART_TO_BE_REORGED); part_elem->part_state= PART_TO_BE_DROPPED; } + DBUG_ASSERT(m_new_file == 0); m_new_file= new_file_array; if (unlikely((error= copy_partitions(copied, deleted)))) { @@ -2077,6 +2078,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, They will later be deleted through the ddl-log. */ cleanup_new_partition(part_count); + m_new_file= 0; } DBUG_RETURN(error); } @@ -2166,6 +2168,8 @@ int ha_partition::copy_partitions(ulonglong * const copied, file->ha_rnd_end(); reorg_part++; } + DBUG_EXECUTE_IF("debug_abort_copy_partitions", + DBUG_RETURN(HA_ERR_UNSUPPORTED); ); DBUG_RETURN(FALSE); error: m_reorged_file[reorg_part]->ha_rnd_end(); diff --git a/sql/handle_connections_win.cc b/sql/handle_connections_win.cc index b12ea583230..debbce998fa 100644 --- a/sql/handle_connections_win.cc +++ b/sql/handle_connections_win.cc @@ -309,6 +309,64 @@ retry : } }; +/* + Create a security descriptor for pipe. + - Use low integrity level, so that it is possible to connect + from any process. + - Give current user read/write access to pipe. + - Give Everyone read/write access to pipe minus FILE_CREATE_PIPE_INSTANCE +*/ +static void init_pipe_security_descriptor() +{ +#define SDDL_FMT "S:(ML;; NW;;; LW) D:(A;; 0x%08x;;; WD)(A;; FRFW;;; %s)" +#define EVERYONE_PIPE_ACCESS_MASK \ + (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES | READ_CONTROL | \ + SYNCHRONIZE | FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) + +#ifndef SECURITY_MAX_SID_STRING_CHARACTERS +/* Old SDK does not have this constant */ +#define SECURITY_MAX_SID_STRING_CHARACTERS 187 +#endif + + /* + Figure out SID of the user that runs the server, then create SDDL string + for pipe permissions, and convert it to the security descriptor. + */ + char sddl_string[sizeof(SDDL_FMT) + 8 + SECURITY_MAX_SID_STRING_CHARACTERS]; + struct + { + TOKEN_USER token_user; + BYTE buffer[SECURITY_MAX_SID_SIZE]; + } token_buffer; + HANDLE token; + DWORD tmp; + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) + goto fail; + + if (!GetTokenInformation(token, TokenUser, &token_buffer, + (DWORD) sizeof(token_buffer), &tmp)) + goto fail; + + CloseHandle(token); + + char *current_user_string_sid; + if (!ConvertSidToStringSid(token_buffer.token_user.User.Sid, + ¤t_user_string_sid)) + goto fail; + + snprintf(sddl_string, sizeof(sddl_string), SDDL_FMT, + EVERYONE_PIPE_ACCESS_MASK, current_user_string_sid); + LocalFree(current_user_string_sid); + + if (ConvertStringSecurityDescriptorToSecurityDescriptor(sddl_string, + SDDL_REVISION_1, &pipe_security.lpSecurityDescriptor, 0)) + return; + +fail: + sql_perror("Can't start server : Initialize security descriptor"); + unireg_abort(1); +} /** Pipe Listener. @@ -337,13 +395,7 @@ struct Pipe_Listener : public Listener { snprintf(pipe_name, sizeof(pipe_name), "\\\\.\\pipe\\%s", mysqld_unix_port); open_mode |= FILE_FLAG_FIRST_PIPE_INSTANCE; - if (!ConvertStringSecurityDescriptorToSecurityDescriptorA( - "S:(ML;; NW;;; LW) D:(A;; FRFW;;; WD)", - 1, &pipe_security.lpSecurityDescriptor, NULL)) - { - sql_perror("Can't start server : Initialize security descriptor"); - unireg_abort(1); - } + init_pipe_security_descriptor(); pipe_security.nLength= sizeof(SECURITY_ATTRIBUTES); pipe_security.bInheritHandle= FALSE; } diff --git a/sql/handler.cc b/sql/handler.cc index 64d13d1601f..01825c13da6 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -188,7 +188,7 @@ private: static int commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, - bool is_real_trans, bool rw_trans); + bool is_real_trans); static plugin_ref ha_default_plugin(THD *thd) @@ -1621,9 +1621,37 @@ int ha_commit_trans(THD *thd, bool all) /* rw_trans is TRUE when we in a transaction changing data */ bool rw_trans= is_real_trans && (rw_ha_count > (thd->is_current_stmt_binlog_disabled()?0U:1U)); + MDL_request mdl_backup; DBUG_PRINT("info", ("is_real_trans: %d rw_trans: %d rw_ha_count: %d", is_real_trans, rw_trans, rw_ha_count)); + if (rw_trans) + { + /* + Acquire a metadata lock which will ensure that COMMIT is blocked + by an active FLUSH TABLES WITH READ LOCK (and vice versa: + COMMIT in progress blocks FTWRL). + + We allow the owner of FTWRL to COMMIT; we assume that it knows + what it does. + */ + MDL_REQUEST_INIT(&mdl_backup, MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT, + MDL_EXPLICIT); + + if (!WSREP(thd)) + { + if (thd->mdl_context.acquire_lock(&mdl_backup, + thd->variables.lock_wait_timeout)) + { + my_error(ER_ERROR_DURING_COMMIT, MYF(0), 1); + ha_rollback_trans(thd, all); + DBUG_RETURN(1); + } + thd->backup_commit_lock= &mdl_backup; + } + DEBUG_SYNC(thd, "ha_commit_trans_after_acquire_commit_lock"); + } + if (rw_trans && opt_readonly && !(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) && @@ -1663,7 +1691,7 @@ int ha_commit_trans(THD *thd, bool all) // Here, the call will not commit inside InnoDB. It is only working // around closing thd->transaction.stmt open by TR_table::open(). if (all) - commit_one_phase_2(thd, false, &thd->transaction->stmt, false, false); + commit_one_phase_2(thd, false, &thd->transaction->stmt, false); } } #endif @@ -1683,7 +1711,7 @@ int ha_commit_trans(THD *thd, bool all) goto wsrep_err; } #endif /* WITH_WSREP */ - error= ha_commit_one_phase(thd, all, rw_trans); + error= ha_commit_one_phase(thd, all); #ifdef WITH_WSREP // Here in case of error we must return 2 for inconsistency if (run_wsrep_hooks && !error) @@ -1720,7 +1748,7 @@ int ha_commit_trans(THD *thd, bool all) if (!is_real_trans) { - error= commit_one_phase_2(thd, all, trans, is_real_trans, rw_trans); + error= commit_one_phase_2(thd, all, trans, is_real_trans); goto done; } @@ -1754,7 +1782,7 @@ int ha_commit_trans(THD *thd, bool all) DEBUG_SYNC(thd, "ha_commit_trans_after_log_and_order"); DBUG_EXECUTE_IF("crash_commit_after_log", DBUG_SUICIDE();); - error= commit_one_phase_2(thd, all, trans, is_real_trans, rw_trans) ? 2 : 0; + error= commit_one_phase_2(thd, all, trans, is_real_trans) ? 2 : 0; #ifdef WITH_WSREP if (run_wsrep_hooks && (error || (error = wsrep_after_commit(thd, all)))) @@ -1828,6 +1856,17 @@ err: thd->rgi_slave->is_parallel_exec); } end: + if (mdl_backup.ticket) + { + /* + We do not always immediately release transactional locks + after ha_commit_trans() (see uses of ha_enable_transaction()), + thus we release the commit blocker lock as soon as it's + not needed. + */ + thd->mdl_context.release_lock(mdl_backup.ticket); + } + thd->backup_commit_lock= 0; #ifdef WITH_WSREP if (wsrep_is_active(thd) && is_real_trans && !error && (rw_ha_count == 0 || all) && @@ -1842,8 +1881,8 @@ end: /** @note - This function does not care about global read lock. A caller should. - However backup locks are handled in commit_one_phase_2. + This function does not care about global read lock or backup locks, + the caller should. @param[in] all Is set in case of explicit commit (COMMIT statement), or implicit commit @@ -1852,7 +1891,7 @@ end: autocommit=1. */ -int ha_commit_one_phase(THD *thd, bool all, bool rw_trans) +int ha_commit_one_phase(THD *thd, bool all) { THD_TRANS *trans=all ? &thd->transaction->all : &thd->transaction->stmt; /* @@ -1878,48 +1917,21 @@ int ha_commit_one_phase(THD *thd, bool all, bool rw_trans) if ((res= thd->wait_for_prior_commit())) DBUG_RETURN(res); } - res= commit_one_phase_2(thd, all, trans, is_real_trans, rw_trans); + res= commit_one_phase_2(thd, all, trans, is_real_trans); DBUG_RETURN(res); } static int -commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans, - bool rw_trans) +commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans) { int error= 0; uint count= 0; Ha_trx_info *ha_info= trans->ha_list, *ha_info_next; - MDL_request mdl_request; - mdl_request.ticket= 0; DBUG_ENTER("commit_one_phase_2"); if (is_real_trans) DEBUG_SYNC(thd, "commit_one_phase_2"); - if (rw_trans) - { - /* - Acquire a metadata lock which will ensure that COMMIT is blocked - by an active FLUSH TABLES WITH READ LOCK (and vice versa: - COMMIT in progress blocks FTWRL). - - We allow the owner of FTWRL to COMMIT; we assume that it knows - what it does. - */ - MDL_REQUEST_INIT(&mdl_request, MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT, - MDL_EXPLICIT); - - if (!WSREP(thd) && - thd->mdl_context.acquire_lock(&mdl_request, - thd->variables.lock_wait_timeout)) - { - my_error(ER_ERROR_DURING_COMMIT, MYF(0), 1); - ha_rollback_trans(thd, all); - DBUG_RETURN(1); - } - DEBUG_SYNC(thd, "ha_commit_trans_after_acquire_commit_lock"); - } - if (ha_info) { for (; ha_info; ha_info= ha_info_next) @@ -1948,16 +1960,6 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans, #endif } } - if (mdl_request.ticket) - { - /* - We do not always immediately release transactional locks - after ha_commit_trans() (see uses of ha_enable_transaction()), - thus we release the commit blocker lock as soon as it's - not needed. - */ - thd->mdl_context.release_lock(mdl_request.ticket); - } /* Free resources and perform other cleanup even for 'empty' transactions. */ if (is_real_trans) @@ -2314,7 +2316,7 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin, for (int i=0; i < got; i ++) { - my_xid x= IF_WSREP(WSREP_ON && wsrep_is_wsrep_xid(&info->list[i]) ? + my_xid x= IF_WSREP(wsrep_is_wsrep_xid(&info->list[i]) ? wsrep_xid_seqno(&info->list[i]) : info->list[i].get_my_xid(), info->list[i].get_my_xid()); @@ -2762,6 +2764,9 @@ int ha_delete_table(THD *thd, handlerton *hton, const char *path, if (hton == NULL || hton == view_pseudo_hton) DBUG_RETURN(0); + if (ha_check_if_updates_are_ignored(thd, hton, "DROP")) + DBUG_RETURN(0); + error= hton->drop_table(hton, path); if (error > 0) { @@ -2799,7 +2804,8 @@ int ha_delete_table(THD *thd, handlerton *hton, const char *path, error= -1; } } - + if (error) + DBUG_PRINT("exit", ("error: %d", error)); DBUG_RETURN(error); } @@ -5006,7 +5012,8 @@ static my_bool delete_table_force(THD *thd, plugin_ref plugin, void *arg) handlerton *hton = plugin_hton(plugin); st_force_drop_table_params *param = (st_force_drop_table_params *)arg; - if (param->discovering == (hton->discover_table != NULL)) + if (param->discovering == (hton->discover_table != NULL) && + !(thd->slave_thread && (hton->flags & HTON_IGNORE_UPDATES))) { int error; error= ha_delete_table(thd, hton, param->path, param->db, param->alias, 0); @@ -6316,6 +6323,7 @@ extern "C" check_result_t handler_index_cond_check(void* h_arg) THD *thd= h->table->in_use; check_result_t res; + DEBUG_SYNC(thd, "handler_index_cond_check"); enum thd_kill_levels abort_at= h->has_rollback() ? THD_ABORT_SOFTLY : THD_ABORT_ASAP; if (thd_kill_level(thd) > abort_at) @@ -6349,6 +6357,7 @@ check_result_t handler_rowid_filter_check(void *h_arg) if (!h->pushed_idx_cond) { THD *thd= h->table->in_use; + DEBUG_SYNC(thd, "handler_rowid_filter_check"); enum thd_kill_levels abort_at= h->has_transactions() ? THD_ABORT_SOFTLY : THD_ABORT_ASAP; if (thd_kill_level(thd) > abort_at) @@ -6938,7 +6947,7 @@ int handler::ha_check_overlaps(const uchar *old_data, const uchar* new_data) uchar *record_buffer= lookup_buffer + table_share->max_unique_length + table_share->null_fields; - // Needs to compare record refs later is old_row_found() + // Needed to compare record refs later if (is_update) position(old_data); @@ -6994,12 +7003,8 @@ int handler::ha_check_overlaps(const uchar *old_data, const uchar* new_data) /* In case of update it could happen that the nearest neighbour is a record we are updating. It means, that there are no overlaps from this side. - - An assumption is made that during update we always have the last - fetched row in old_data. Therefore, comparing ref's is enough */ DBUG_ASSERT(lookup_handler != this); - DBUG_ASSERT(inited != NONE); DBUG_ASSERT(ref_length == lookup_handler->ref_length); lookup_handler->position(record_buffer); @@ -7124,16 +7129,17 @@ int handler::ha_write_row(const uchar *buf) if ((error= ha_check_overlaps(NULL, buf))) DBUG_RETURN(error); - MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str); - mark_trx_read_write(); - increment_statistics(&SSV::ha_write_count); - if (table->s->long_unique_table && this == table->file) { DBUG_ASSERT(inited == NONE || lookup_handler != this); if ((error= check_duplicate_long_entries(buf))) DBUG_RETURN(error); } + + MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str); + mark_trx_read_write(); + increment_statistics(&SSV::ha_write_count); + TABLE_IO_WAIT(tracker, PSI_TABLE_WRITE_ROW, MAX_KEY, error, { error= write_row(buf); }) @@ -7173,17 +7179,19 @@ int handler::ha_update_row(const uchar *old_data, const uchar *new_data) DBUG_ASSERT(new_data == table->record[0]); DBUG_ASSERT(old_data == table->record[1]); - if ((error= ha_check_overlaps(old_data, new_data))) + uint saved_status= table->status; + error= ha_check_overlaps(old_data, new_data); + + if (!error && table->s->long_unique_table && this == table->file) + error= check_duplicate_long_entries_update(new_data); + table->status= saved_status; + + if (error) return error; MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str); mark_trx_read_write(); increment_statistics(&SSV::ha_update_count); - if (table->s->long_unique_table && this == table->file && - (error= check_duplicate_long_entries_update(new_data))) - { - return error; - } TABLE_IO_WAIT(tracker, PSI_TABLE_UPDATE_ROW, active_index, 0, { error= update_row(old_data, new_data);}) @@ -8075,6 +8083,8 @@ Vers_parse_info::fix_create_like(Alter_info &alter_info, HA_CREATE_INFO &create_ TABLE_LIST &src_table, TABLE_LIST &table) { List_iterator<Create_field> it(alter_info.create_list); + List_iterator<Key> key_it(alter_info.key_list); + List_iterator<Key_part_spec> kp_it; Create_field *f, *f_start=NULL, *f_end= NULL; DBUG_ASSERT(alter_info.create_list.elements > 2); @@ -8089,6 +8099,23 @@ Vers_parse_info::fix_create_like(Alter_info &alter_info, HA_CREATE_INFO &create_ it.remove(); remove--; } + key_it.rewind(); + while (Key *key= key_it++) + { + kp_it.init(key->columns); + while (Key_part_spec *kp= kp_it++) + { + if (0 == lex_string_cmp(system_charset_info, &kp->field_name, + &f->field_name)) + { + kp_it.remove(); + } + } + if (0 == key->columns.elements) + { + key_it.remove(); + } + } } DBUG_ASSERT(remove == 0); push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, diff --git a/sql/handler.h b/sql/handler.h index 9ab84794277..b106b460c70 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -5179,7 +5179,7 @@ int ha_change_key_cache(KEY_CACHE *old_key_cache, KEY_CACHE *new_key_cache); /* transactions: interface to handlerton functions */ int ha_start_consistent_snapshot(THD *thd); int ha_commit_or_rollback_by_xid(XID *xid, bool commit); -int ha_commit_one_phase(THD *thd, bool all, bool rw_trans); +int ha_commit_one_phase(THD *thd, bool all); int ha_commit_trans(THD *thd, bool all); int ha_rollback_trans(THD *thd, bool all); int ha_prepare(THD *thd); diff --git a/sql/item.h b/sql/item.h index 19b7b5d03e9..fb480b4c578 100644 --- a/sql/item.h +++ b/sql/item.h @@ -120,20 +120,19 @@ enum precedence { XOR_PRECEDENCE, // XOR AND_PRECEDENCE, // AND, && NOT_PRECEDENCE, // NOT (unless HIGH_NOT_PRECEDENCE) - BETWEEN_PRECEDENCE, // BETWEEN, CASE, WHEN, THEN, ELSE - CMP_PRECEDENCE, // =, <=>, >=, >, <=, <, <>, !=, IS, LIKE, REGEXP, IN + CMP_PRECEDENCE, // =, <=>, >=, >, <=, <, <>, !=, IS + BETWEEN_PRECEDENCE, // BETWEEN + IN_PRECEDENCE, // IN, LIKE, REGEXP BITOR_PRECEDENCE, // | BITAND_PRECEDENCE, // & SHIFT_PRECEDENCE, // <<, >> - ADDINTERVAL_PRECEDENCE, // first argument in +INTERVAL + INTERVAL_PRECEDENCE, // first argument in +INTERVAL ADD_PRECEDENCE, // +, - MUL_PRECEDENCE, // *, /, DIV, %, MOD BITXOR_PRECEDENCE, // ^ PIPES_PRECEDENCE, // || (if PIPES_AS_CONCAT) - NEG_PRECEDENCE, // unary -, ~ - BANG_PRECEDENCE, // !, NOT (if HIGH_NOT_PRECEDENCE) + NEG_PRECEDENCE, // unary -, ~, !, NOT (if HIGH_NOT_PRECEDENCE) COLLATE_PRECEDENCE, // BINARY, COLLATE - INTERVAL_PRECEDENCE, // INTERVAL DEFAULT_PRECEDENCE, HIGHEST_PRECEDENCE }; @@ -1716,6 +1715,8 @@ public: mysql_register_view(). */ virtual enum precedence precedence() const { return DEFAULT_PRECEDENCE; } + enum precedence higher_precedence() const + { return (enum precedence)(precedence() + 1); } void print_parenthesised(String *str, enum_query_type query_type, enum precedence parent_prec); /** @@ -5439,7 +5440,11 @@ public: { (*ref)->restore_to_before_no_rows_in_result(); } - virtual void print(String *str, enum_query_type query_type); + void print(String *str, enum_query_type query_type); + enum precedence precedence() const + { + return ref ? (*ref)->precedence() : DEFAULT_PRECEDENCE; + } void cleanup(); Item_field *field_for_view_update() { return (*ref)->field_for_view_update(); } diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index c753b963fd4..83eb605f463 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -868,6 +868,8 @@ int Arg_comparator::compare_decimal() { if (set_null) owner->null_value= 0; + val1.round_self_if_needed((*a)->decimals, HALF_UP); + val2.round_self_if_needed((*b)->decimals, HALF_UP); return val1.cmp(val2); } } @@ -890,6 +892,8 @@ int Arg_comparator::compare_e_decimal() VDec val1(*a), val2(*b); if (val1.is_null() || val2.is_null()) return MY_TEST(val1.is_null() && val2.is_null()); + val1.round_self_if_needed((*a)->decimals, HALF_UP); + val2.round_self_if_needed((*b)->decimals, HALF_UP); return MY_TEST(val1.cmp(val2) == 0); } @@ -2331,7 +2335,7 @@ longlong Item_func_between::val_int_cmp_real() void Item_func_between::print(String *str, enum_query_type query_type) { - args[0]->print_parenthesised(str, query_type, precedence()); + args[0]->print_parenthesised(str, query_type, higher_precedence()); if (negated) str->append(STRING_WITH_LEN(" not")); str->append(STRING_WITH_LEN(" between ")); @@ -3348,27 +3352,28 @@ Item* Item_func_case_simple::propagate_equal_fields(THD *thd, } -void Item_func_case::print_when_then_arguments(String *str, - enum_query_type query_type, - Item **items, uint count) +inline void Item_func_case::print_when_then_arguments(String *str, + enum_query_type + query_type, + Item **items, uint count) { - for (uint i=0 ; i < count ; i++) + for (uint i= 0; i < count; i++) { str->append(STRING_WITH_LEN("when ")); - items[i]->print_parenthesised(str, query_type, precedence()); + items[i]->print(str, query_type); str->append(STRING_WITH_LEN(" then ")); - items[i + count]->print_parenthesised(str, query_type, precedence()); + items[i + count]->print(str, query_type); str->append(' '); } } -void Item_func_case::print_else_argument(String *str, - enum_query_type query_type, - Item *item) +inline void Item_func_case::print_else_argument(String *str, + enum_query_type query_type, + Item *item) { str->append(STRING_WITH_LEN("else ")); - item->print_parenthesised(str, query_type, precedence()); + item->print(str, query_type); str->append(' '); } @@ -5600,12 +5605,14 @@ void Item_func_like::print(String *str, enum_query_type query_type) str->append(STRING_WITH_LEN(" not ")); str->append(func_name()); str->append(' '); - args[1]->print_parenthesised(str, query_type, precedence()); if (escape_used_in_parsing) { + args[1]->print_parenthesised(str, query_type, precedence()); str->append(STRING_WITH_LEN(" escape ")); - escape_item->print(str, query_type); + escape_item->print_parenthesised(str, query_type, higher_precedence()); } + else + args[1]->print_parenthesised(str, query_type, higher_precedence()); } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index fe6cba607a7..fa715badfc7 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1,7 +1,7 @@ #ifndef ITEM_CMPFUNC_INCLUDED #define ITEM_CMPFUNC_INCLUDED /* Copyright (c) 2000, 2015, Oracle and/or its affiliates. - Copyright (c) 2009, 2016, MariaDB + Copyright (c) 2009, 2020, 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 @@ -608,7 +608,7 @@ public: enum Functype functype() const override { return NOT_FUNC; } const char *func_name() const override { return "not"; } bool find_not_null_fields(table_map allowed) override { return false; } - enum precedence precedence() const override { return BANG_PRECEDENCE; } + enum precedence precedence() const override { return NEG_PRECEDENCE; } Item *neg_transformer(THD *thd) override; bool fix_fields(THD *, Item **) override; void print(String *str, enum_query_type query_type) override; @@ -2184,9 +2184,11 @@ protected: bool aggregate_then_and_else_arguments(THD *thd, uint count); virtual Item **else_expr_addr() const= 0; virtual Item *find_item()= 0; - void print_when_then_arguments(String *str, enum_query_type query_type, - Item **items, uint count); - void print_else_argument(String *str, enum_query_type query_type, Item *item); + inline void print_when_then_arguments(String *str, + enum_query_type query_type, + Item **items, uint count); + inline void print_else_argument(String *str, enum_query_type query_type, + Item *item); void reorder_args(uint start); public: Item_func_case(THD *thd, List<Item> &list) @@ -2202,7 +2204,6 @@ public: bool fix_fields(THD *thd, Item **ref); table_map not_null_tables() const { return 0; } const char *func_name() const { return "case"; } - enum precedence precedence() const { return BETWEEN_PRECEDENCE; } CHARSET_INFO *compare_collation() const { return cmp_collation.collation; } bool need_parentheses_in_default() { return true; } }; @@ -2467,7 +2468,7 @@ public: virtual void print(String *str, enum_query_type query_type); enum Functype functype() const { return IN_FUNC; } const char *func_name() const { return "in"; } - enum precedence precedence() const { return CMP_PRECEDENCE; } + enum precedence precedence() const { return IN_PRECEDENCE; } bool eval_not_null_tables(void *opt_arg); bool find_not_null_fields(table_map allowed); void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge); @@ -2793,7 +2794,7 @@ public: return this; } const char *func_name() const { return "like"; } - enum precedence precedence() const { return CMP_PRECEDENCE; } + enum precedence precedence() const { return IN_PRECEDENCE; } bool fix_fields(THD *thd, Item **ref); bool fix_length_and_dec() { @@ -2902,7 +2903,7 @@ public: longlong val_int(); bool fix_length_and_dec(); const char *func_name() const { return "regexp"; } - enum precedence precedence() const { return CMP_PRECEDENCE; } + enum precedence precedence() const { return IN_PRECEDENCE; } Item *get_copy(THD *) { return 0; } void print(String *str, enum_query_type query_type) { diff --git a/sql/item_func.cc b/sql/item_func.cc index 8a75d2c9946..027488e0279 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -640,8 +640,7 @@ void Item_func::print_op(String *str, enum_query_type query_type) str->append(func_name()); str->append(' '); } - args[arg_count-1]->print_parenthesised(str, query_type, - (enum precedence)(precedence() + 1)); + args[arg_count-1]->print_parenthesised(str, query_type, higher_precedence()); } @@ -1493,14 +1492,13 @@ double Item_func_div::real_op() my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value) { int err; - my_decimal tmp; VDec2_lazy val(args[0], args[1]); if ((null_value= val.has_null())) return 0; if ((err= check_decimal_overflow(my_decimal_div(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW & ~E_DEC_DIV_ZERO, - &tmp, + decimal_value, val.m_a.ptr(), val.m_b.ptr(), prec_increment))) > 3) { @@ -1509,7 +1507,6 @@ my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value) null_value= 1; return 0; } - tmp.round_to(decimal_value, decimals, HALF_UP); return decimal_value; } @@ -4017,6 +4014,8 @@ int Interruptible_wait::wait(mysql_cond_t *cond, mysql_mutex_t *mutex) timeout= m_abs_timeout; error= mysql_cond_timedwait(cond, mutex, &timeout); + if (m_thd->check_killed()) + break; if (error == ETIMEDOUT || error == ETIME) { /* Return error if timed out or connection is broken. */ diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 4acf1c6525b..f1a4ac4ef8a 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2003, 2016, Oracle and/or its affiliates. - Copyright (c) 2011, 2016, MariaDB + Copyright (c) 2011, 2020, 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 @@ -443,16 +443,18 @@ String *Item_func_boundary::val_str(String *str_value) DBUG_ASSERT(fixed == 1); String arg_val; String *swkb= args[0]->val_str(&arg_val); + + if ((null_value= args[0]->null_value)) + DBUG_RETURN(0); + Geometry_buffer buffer; - Geometry *g; uint32 srid= 0; Transporter trn(&res_receiver); - - if ((null_value= - args[0]->null_value || - !(g= Geometry::construct(&buffer, swkb->ptr(), swkb->length())))) + + Geometry *g= Geometry::construct(&buffer, swkb->ptr(), swkb->length()); + if (!g) DBUG_RETURN(0); - + if (g->store_shapes(&trn)) goto mem_error; diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index aec3e1edcd3..032ecb1bb91 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -2464,6 +2464,8 @@ String *Item_func_json_merge_patch::val_str(String *str) uint n_arg; bool empty_result, merge_to_null; + /* To report errors properly if some JSON is invalid. */ + je1.s.error= je2.s.error= 0; merge_to_null= args[0]->null_value; for (n_arg=1; n_arg < arg_count; n_arg++) @@ -3772,6 +3774,8 @@ Item_func_json_objectagg::fix_fields(THD *thd, Item **ref) uint i; /* for loop variable */ DBUG_ASSERT(fixed == 0); + memcpy(orig_args, args, sizeof(Item*) * arg_count); + if (init_sum_func_check(thd)) return TRUE; @@ -3867,7 +3871,3 @@ String* Item_func_json_objectagg::val_str(String* str) } -void Item_func_json_objectagg::print(String *str, enum_query_type query_type) -{ -} - diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h index 6bd2a81afc6..ec6c6696001 100644 --- a/sql/item_jsonfunc.h +++ b/sql/item_jsonfunc.h @@ -589,7 +589,7 @@ public: void cleanup(); enum Sumfunctype sum_func () const {return JSON_OBJECTAGG_FUNC;} - const char *func_name() const { return "json_objectagg("; } + const char *func_name() const { return "json_objectagg"; } const Type_handler *type_handler() const { if (too_big_for_varchar()) @@ -618,7 +618,6 @@ public: String* val_str(String* str); Item *copy_or_same(THD* thd); void no_rows_in_result() {} - void print(String *str, enum_query_type query_type); Item *get_copy(THD *thd) { return get_item_copy<Item_func_json_objectagg>(thd, this); } }; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 334fec2e048..cbde9599073 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -3357,7 +3357,7 @@ void Item_in_subselect::print(String *str, enum_query_type query_type) str->append(STRING_WITH_LEN("<exists>")); else { - left_expr->print(str, query_type); + left_expr->print_parenthesised(str, query_type, precedence()); str->append(STRING_WITH_LEN(" in ")); } Item_subselect::print(str, query_type); diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 7e504d09565..ec4398b9a76 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -640,7 +640,7 @@ public: bool val_bool() override; bool test_limit(st_select_lex_unit *unit); void print(String *str, enum_query_type query_type) override; - enum precedence precedence() const override { return CMP_PRECEDENCE; } + enum precedence precedence() const override { return IN_PRECEDENCE; } bool fix_fields(THD *thd, Item **ref) override; bool fix_length_and_dec() override; void fix_after_pullout(st_select_lex *new_parent, Item **ref, diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 6b5c6fa323c..b8650a5c7e9 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2095,9 +2095,9 @@ static const char *interval_names[]= void Item_date_add_interval::print(String *str, enum_query_type query_type) { - args[0]->print_parenthesised(str, query_type, ADDINTERVAL_PRECEDENCE); + args[0]->print_parenthesised(str, query_type, INTERVAL_PRECEDENCE); str->append(date_sub_interval?" - interval ":" + interval "); - args[1]->print_parenthesised(str, query_type, INTERVAL_PRECEDENCE); + args[1]->print(str, query_type); str->append(' '); str->append(interval_names[int_type]); } diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 35c9df533c4..a910b2cb723 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -948,7 +948,7 @@ public: bool fix_length_and_dec(); bool eq(const Item *item, bool binary_cmp) const; void print(String *str, enum_query_type query_type); - enum precedence precedence() const { return ADDINTERVAL_PRECEDENCE; } + enum precedence precedence() const { return INTERVAL_PRECEDENCE; } bool need_parentheses_in_default() { return true; } Item *get_copy(THD *thd) { return get_item_copy<Item_date_add_interval>(thd, this); } diff --git a/sql/log.cc b/sql/log.cc index 2a887a68606..2dd1a3a45ab 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -601,9 +601,11 @@ bool LOGGER::is_log_table_enabled(uint log_table_type) { switch (log_table_type) { case QUERY_LOG_SLOW: - return (table_log_handler != NULL) && global_system_variables.sql_log_slow; + return (table_log_handler != NULL) && global_system_variables.sql_log_slow + && (log_output_options & LOG_TABLE); case QUERY_LOG_GENERAL: - return (table_log_handler != NULL) && opt_log ; + return (table_log_handler != NULL) && opt_log + && (log_output_options & LOG_TABLE); default: DBUG_ASSERT(0); return FALSE; /* make compiler happy */ @@ -1355,7 +1357,7 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, size_t query_length, my_hrtime_t current_time= { hrtime_from_time(thd->start_time) + thd->start_time_sec_part + query_utime }; - if (!query) + if (!query || thd->get_command() == COM_STMT_PREPARE) { is_command= TRUE; query= command_name[thd->get_command()].str; @@ -2151,6 +2153,12 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all) DBUG_RETURN(0); } + /* + This is true if we are doing an alter table that is replicated as + CREATE TABLE ... SELECT + */ + if (thd->variables.option_bits & OPTION_BIN_COMMIT_OFF) + DBUG_RETURN(0); DBUG_PRINT("debug", ("all: %d, in_transaction: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s", @@ -6574,11 +6582,26 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) if (direct) { + /* We come here only for incident events */ int res; uint64 commit_id= 0; + MDL_request mdl_request; DBUG_PRINT("info", ("direct is set")); + DBUG_ASSERT(!thd->backup_commit_lock); + + MDL_REQUEST_INIT(&mdl_request, MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT, + MDL_EXPLICIT); + thd->mdl_context.acquire_lock(&mdl_request, + thd->variables.lock_wait_timeout); + thd->backup_commit_lock= &mdl_request; + if ((res= thd->wait_for_prior_commit())) + { + if (mdl_request.ticket) + thd->mdl_context.release_lock(mdl_request.ticket); + thd->backup_commit_lock= 0; DBUG_RETURN(res); + } file= &log_file; my_org_b_tell= my_b_tell(file); mysql_mutex_lock(&LOCK_log); @@ -6593,7 +6616,11 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) commit_name.length); commit_id= entry->val_int(&null_value); }); - if (write_gtid_event(thd, true, using_trans, commit_id)) + res= write_gtid_event(thd, true, using_trans, commit_id); + if (mdl_request.ticket) + thd->mdl_context.release_lock(mdl_request.ticket); + thd->backup_commit_lock= 0; + if (res) goto err; } else @@ -7657,7 +7684,11 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry) group_commit_entry *entry, *orig_queue, *last; wait_for_commit *cur; wait_for_commit *wfc; + bool backup_lock_released= 0; + int result= 0; + THD *thd= orig_entry->thd; DBUG_ENTER("MYSQL_BIN_LOG::queue_for_group_commit"); + DBUG_ASSERT(thd == current_thd); /* Check if we need to wait for another transaction to commit before us. @@ -7689,6 +7720,21 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry) { PSI_stage_info old_stage; + /* + Release MDL_BACKUP_COMMIT LOCK while waiting for other threads to + commit. + This is needed to avoid deadlock between the other threads (which not + yet have the MDL_BACKUP_COMMIT_LOCK) and any threads using + BACKUP LOCK BLOCK_COMMIT. + */ + if (thd->backup_commit_lock && thd->backup_commit_lock->ticket && + !backup_lock_released) + { + backup_lock_released= 1; + thd->mdl_context.release_lock(thd->backup_commit_lock->ticket); + thd->backup_commit_lock->ticket= 0; + } + /* By setting wfc->opaque_pointer to our own entry, we mark that we are ready to commit, but waiting for another transaction to commit before @@ -7749,7 +7795,8 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry) wfc->wakeup_error= ER_QUERY_INTERRUPTED; my_message(wfc->wakeup_error, ER_THD(orig_entry->thd, wfc->wakeup_error), MYF(0)); - DBUG_RETURN(-1); + result= -1; + goto end; } } orig_entry->thd->EXIT_COND(&old_stage); @@ -7763,12 +7810,13 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry) then there is nothing else to do. */ if (orig_entry->queued_by_other) - DBUG_RETURN(0); + goto end; if (wfc && wfc->wakeup_error) { my_error(ER_PRIOR_COMMIT_FAILED, MYF(0)); - DBUG_RETURN(-1); + result= -1; + goto end; } /* Now enqueue ourselves in the group commit queue. */ @@ -7929,7 +7977,13 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry) DBUG_PRINT("info", ("Queued for group commit as %s", (orig_queue == NULL) ? "leader" : "participant")); - DBUG_RETURN(orig_queue == NULL); + result= orig_queue == NULL; + +end: + if (backup_lock_released) + thd->mdl_context.acquire_lock(thd->backup_commit_lock, + thd->variables.lock_wait_timeout); + DBUG_RETURN(result); } bool @@ -10753,7 +10807,8 @@ binlog_checksum_update(MYSQL_THD thd, struct st_mysql_sys_var *var, } -static int show_binlog_vars(THD *thd, SHOW_VAR *var, char *buff) +static int show_binlog_vars(THD *thd, SHOW_VAR *var, void *, + system_status_var *status_var, enum_var_type) { mysql_bin_log.set_status_variables(thd); var->type= SHOW_ARRAY; diff --git a/sql/log_event.cc b/sql/log_event.cc index d66ece96eba..0388a2b19b1 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1074,7 +1074,7 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len, else DBUG_RETURN(NULL); #else - *error= ER(ER_BINLOG_READ_EVENT_CHECKSUM_FAILURE); + *error= ER_THD_OR_DEFAULT(current_thd, ER_BINLOG_READ_EVENT_CHECKSUM_FAILURE); sql_print_error("%s", *error); DBUG_RETURN(NULL); #endif diff --git a/sql/log_event.h b/sql/log_event.h index 222e611ecae..4e193232f4b 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -2227,8 +2227,10 @@ public: virtual bool is_commit() { return false; } virtual bool is_rollback() { return false; } #ifdef MYSQL_SERVER - Query_compressed_log_event(THD* thd_arg, const char* query_arg, ulong query_length, - bool using_trans, bool direct, bool suppress_use, int error); + Query_compressed_log_event(THD* thd_arg, const char* query_arg, + ulong query_length, + bool using_trans, bool direct, bool suppress_use, + int error); virtual bool write(); #endif }; diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc index 4c26acd2cc1..0480a8fa59f 100644 --- a/sql/log_event_client.cc +++ b/sql/log_event_client.cc @@ -899,6 +899,16 @@ log_event_print_value(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info, fprintf(stderr, "\nError: Found Old DECIMAL (mysql-4.1 or earlier). " "Not enough metadata to display the value.\n"); break; + + case MYSQL_TYPE_GEOMETRY: + strmake(typestr, "GEOMETRY", typestr_length); + if (!ptr) + goto return_null; + + length= uint4korr(ptr); + my_b_write_quoted(file, ptr + meta, length); + return length + meta; + default: print_event_info->flush_for_error(); fprintf(stderr, diff --git a/sql/mf_iocache_encr.cc b/sql/mf_iocache_encr.cc index b27f10c7d72..072c0a48269 100644 --- a/sql/mf_iocache_encr.cc +++ b/sql/mf_iocache_encr.cc @@ -71,6 +71,16 @@ static int my_b_encr_read(IO_CACHE *info, uchar *Buffer, size_t Count) DBUG_RETURN(1); } info->seek_not_done= 0; + if (info->next_file_user) + { + IO_CACHE *c; + for (c= info->next_file_user; + c!= info; + c= c->next_file_user) + { + c->seek_not_done= 1; + } + } } do diff --git a/sql/my_decimal.h b/sql/my_decimal.h index 2db08bf01e3..4c1f41463d5 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -217,15 +217,15 @@ public: { return to_string(to, 0, 0, 0); } - String *to_string_round(String *to, uint scale, my_decimal *round_buff) const + String *to_string_round(String *to, int scale, my_decimal *round_buff) const { (void) round_to(round_buff, scale, HALF_UP); // QQ: check result? return round_buff->to_string(to); } - int round_to(my_decimal *to, uint scale, decimal_round_mode mode, + int round_to(my_decimal *to, int scale, decimal_round_mode mode, int mask= E_DEC_FATAL_ERROR) const { - return check_result(mask, decimal_round(this, to, (int) scale, mode)); + return check_result(mask, decimal_round(this, to, scale, mode)); } int to_binary(uchar *bin, int prec, int scale, uint mask= E_DEC_FATAL_ERROR) const; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 99ec7c1eb56..8417208d68f 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1884,13 +1884,10 @@ extern "C" void unireg_abort(int exit_code) WSREP_INFO("Some threads may fail to exit."); } - if (WSREP_ON) + if (WSREP_ON && wsrep_inited) { - /* In bootstrap mode we deinitialize wsrep here. */ - if (opt_bootstrap || wsrep_recovery) - { - if (wsrep_inited) wsrep_deinit(true); - } + wsrep_deinit(true); + wsrep_deinit_server(); } #endif // WITH_WSREP @@ -6950,8 +6947,8 @@ show_ssl_get_server_not_after(THD *thd, SHOW_VAR *var, char *buff, #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ -static int show_default_keycache(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_default_keycache(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { struct st_data { KEY_CACHE_STATISTICS stats; @@ -6984,7 +6981,7 @@ static int show_default_keycache(THD *thd, SHOW_VAR *var, char *buff, v->name= 0; - DBUG_ASSERT((char*)(v+1) <= buff + SHOW_VAR_FUNC_BUFF_SIZE); + DBUG_ASSERT((char*)(v+1) <= static_cast<char*>(buff) + SHOW_VAR_FUNC_BUFF_SIZE); #undef set_one_keycache_var @@ -7008,8 +7005,8 @@ static int show_memory_used(THD *thd, SHOW_VAR *var, char *buff, #ifndef DBUG_OFF -static int debug_status_func(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int debug_status_func(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { #define add_var(X,Y,Z) \ v->name= X; \ diff --git a/sql/opt_range.cc b/sql/opt_range.cc index c674730d230..c13563f9263 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1872,6 +1872,9 @@ SEL_ARG::SEL_ARG(SEL_ARG &arg) :Sql_alloc() next_key_part=arg.next_key_part; max_part_no= arg.max_part_no; use_count=1; elements=1; + next= 0; + if (next_key_part) + ++next_key_part->use_count; } @@ -9616,9 +9619,15 @@ tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2) } bool no_imerge_from_ranges= FALSE; + SEL_TREE *rt1= tree1; + SEL_TREE *rt2= tree2; /* Build the range part of the tree for the formula (1) */ if (sel_trees_can_be_ored(param, tree1, tree2, &ored_keys)) { + if (no_merges1) + rt1= new SEL_TREE(tree1, TRUE, param); + if (no_merges2) + rt2= new SEL_TREE(tree2, TRUE, param); bool must_be_ored= sel_trees_must_be_ored(param, tree1, tree2, ored_keys); no_imerge_from_ranges= must_be_ored; @@ -9676,12 +9685,6 @@ tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2) else if (!no_ranges1 && !no_ranges2 && !no_imerge_from_ranges) { /* Build the imerge part of the tree for the formula (1) */ - SEL_TREE *rt1= tree1; - SEL_TREE *rt2= tree2; - if (no_merges1) - rt1= new SEL_TREE(tree1, TRUE, param); - if (no_merges2) - rt2= new SEL_TREE(tree2, TRUE, param); if (!rt1 || !rt2 || result->merges.push_back(imerge_from_ranges) || imerge_from_ranges->or_sel_tree(param, rt1) || @@ -10346,10 +10349,11 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2) if (!tmp->next_key_part) { + SEL_ARG *key2_next= key2->next; if (key2->use_count) { SEL_ARG *key2_cpy= new SEL_ARG(*key2); - if (key2_cpy) + if (!key2_cpy) return 0; key2= key2_cpy; } @@ -10370,7 +10374,7 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2) Move on to next range in key2 */ key2->increment_use_count(-1); // Free not used tree - key2=key2->next; + key2=key2_next; continue; } else diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 8ae3ce5c995..b7f5ffd02d1 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2010, 2019, MariaDB + Copyright (c) 2010, 2020, 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 @@ -2681,16 +2681,11 @@ bool find_eq_ref_candidate(TABLE *table, table_map sj_inner_tables) do { uint key= keyuse->key; - KEY *keyinfo; key_part_map bound_parts= 0; - bool is_excluded_key= keyuse->is_for_hash_join(); - if (!is_excluded_key) - { - keyinfo= table->key_info + key; - is_excluded_key= !MY_TEST(keyinfo->flags & HA_NOSAME); - } - if (!is_excluded_key) + if (!keyuse->is_for_hash_join() && + (table->key_info[key].flags & HA_NOSAME)) { + KEY *keyinfo= table->key_info + key; do /* For all equalities on all key parts */ { /* diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 0a3c30a176d..af2d9ddc2e7 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -399,20 +399,28 @@ int opt_sum_query(THD *thd, break; } longlong info_limit= 1; - table->file->info_push(INFO_KIND_FORCE_LIMIT_BEGIN, &info_limit); - if (likely(!(error= table->file->ha_index_init((uint) ref.key, 1)))) - error= (is_max ? - get_index_max_value(table, &ref, range_fl) : - get_index_min_value(table, &ref, item_field, range_fl, - prefix_len)); + error= 0; + table->file->info_push(INFO_KIND_FORCE_LIMIT_BEGIN, &info_limit); + if (!table->const_table) + { + if (likely(!(error= table->file->ha_index_init((uint) ref.key, + 1)))) + error= (is_max ? + get_index_max_value(table, &ref, range_fl) : + get_index_min_value(table, &ref, item_field, range_fl, + prefix_len)); + } /* Verify that the read tuple indeed matches the search key */ if (!error && reckey_in_range(is_max, &ref, item_field->field, conds, range_fl, prefix_len)) error= HA_ERR_KEY_NOT_FOUND; - table->file->ha_end_keyread(); - table->file->ha_index_end(); + if (!table->const_table) + { + table->file->ha_end_keyread(); + table->file->ha_index_end(); + } table->file->info_push(INFO_KIND_FORCE_LIMIT_END, NULL); if (error) { diff --git a/sql/protocol.cc b/sql/protocol.cc index d5e6a7b92bf..65443cafa65 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2012, Oracle and/or its affiliates. - Copyright (c) 2008, 2012, Monty Program Ab + Copyright (c) 2008, 2020, 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 @@ -57,7 +57,8 @@ bool Protocol_binary::net_store_data(const uchar *from, size_t length) packet->realloc(packet_length+9+length)) return 1; uchar *to= net_store_length((uchar*) packet->ptr()+packet_length, length); - memcpy(to,from,length); + if (length) + memcpy(to,from,length); packet->length((uint) (to+length-(uchar*) packet->ptr())); return 0; } @@ -730,7 +731,8 @@ void net_send_progress_packet(THD *thd) uchar *net_store_data(uchar *to, const uchar *from, size_t length) { to=net_store_length_fast(to,length); - memcpy(to,from,length); + if (length) + memcpy(to,from,length); return to+length; } diff --git a/sql/select_handler.cc b/sql/select_handler.cc index 9a2f6398a86..795ed8eb641 100644 --- a/sql/select_handler.cc +++ b/sql/select_handler.cc @@ -21,10 +21,10 @@ /** - The methods of the Pushdown_select class. + The methods of the select_handler class. The objects of this class are used for pushdown of the select queries - into engines. The main method of the class is Pushdown_select::execute() + into engines. The main method of the class is select_handler::execute() that initiates execution of a select query by a foreign engine, receives the rows of the result set, put it in a buffer of a temporary table and send them from the buffer directly into output. @@ -36,51 +36,55 @@ */ -Pushdown_select::Pushdown_select(SELECT_LEX *sel, select_handler *h) - : select(sel), handler(h) -{ - is_analyze= handler->thd->lex->analyze_stmt; -} +select_handler::select_handler(THD *thd_arg, handlerton *ht_arg) + : thd(thd_arg), ht(ht_arg), table(NULL), + is_analyze(thd_arg->lex->analyze_stmt) +{} -Pushdown_select::~Pushdown_select() +select_handler::~select_handler() { - if (handler->table) - free_tmp_table(handler->thd, handler->table); - delete handler; - select->select_h= NULL; + if (table) + free_tmp_table(thd, table); } -bool Pushdown_select::init() +TABLE *select_handler::create_tmp_table(THD *thd, SELECT_LEX *select) { + DBUG_ENTER("select_handler::create_tmp_table"); List<Item> types; TMP_TABLE_PARAM tmp_table_param; - THD *thd= handler->thd; - DBUG_ENTER("Pushdown_select::init"); if (select->master_unit()->join_union_item_types(thd, types, 1)) - DBUG_RETURN(true); + DBUG_RETURN(NULL); tmp_table_param.init(); tmp_table_param.field_count= types.elements; - handler->table= create_tmp_table(thd, &tmp_table_param, types, + TABLE *table= ::create_tmp_table(thd, &tmp_table_param, types, (ORDER *) 0, false, 0, TMP_TABLE_ALL_COLUMNS, 1, &empty_clex_str, true, false); - if (!handler->table) - DBUG_RETURN(true); - if (handler->table->fill_item_list(&result_columns)) + DBUG_RETURN(table); +} + + +bool select_handler::prepare() +{ + DBUG_ENTER("select_handler::prepare"); + /* + Some engines (e.g. XPand) initialize "table" on their own. + So we need to create a temporary table only if "table" is NULL. + */ + if (!table && !(table= create_tmp_table(thd, select))) DBUG_RETURN(true); - DBUG_RETURN(false); + DBUG_RETURN(table->fill_item_list(&result_columns)); } -bool Pushdown_select::send_result_set_metadata() +bool select_handler::send_result_set_metadata() { - DBUG_ENTER("Pushdown_select::send_result_set_metadata"); + DBUG_ENTER("select_handler::send_result_set_metadata"); #ifdef WITH_WSREP - THD *thd= handler->thd; if (WSREP(thd) && thd->wsrep_retry_query) { WSREP_DEBUG("skipping select metadata"); @@ -96,7 +100,7 @@ bool Pushdown_select::send_result_set_metadata() } -bool Pushdown_select::send_data() +bool select_handler::send_data() { DBUG_ENTER("Pushdown_select::send_data"); @@ -107,9 +111,9 @@ bool Pushdown_select::send_data() } -bool Pushdown_select::send_eof() +bool select_handler::send_eof() { - DBUG_ENTER("Pushdown_select::send_eof"); + DBUG_ENTER("select_handler::send_eof"); if (select->join->result->send_eof()) DBUG_RETURN(true); @@ -117,30 +121,29 @@ bool Pushdown_select::send_eof() } -int Pushdown_select::execute() +int select_handler::execute() { int err; - THD *thd= handler->thd; - DBUG_ENTER("Pushdown_select::execute"); + DBUG_ENTER("select_handler::execute"); - if ((err= handler->init_scan())) + if ((err= init_scan())) goto error; if (is_analyze) { - handler->end_scan(); + end_scan(); DBUG_RETURN(0); } if (send_result_set_metadata()) DBUG_RETURN(-1); - while (!(err= handler->next_row())) + while (!(err= next_row())) { if (thd->check_killed() || send_data()) { - handler->end_scan(); + end_scan(); DBUG_RETURN(-1); } } @@ -148,7 +151,7 @@ int Pushdown_select::execute() if (err != 0 && err != HA_ERR_END_OF_FILE) goto error; - if ((err= handler->end_scan())) + if ((err= end_scan())) goto error_2; if (send_eof()) @@ -157,9 +160,9 @@ int Pushdown_select::execute() DBUG_RETURN(0); error: - handler->end_scan(); + end_scan(); error_2: - handler->print_error(err, MYF(0)); + print_error(err, MYF(0)); DBUG_RETURN(-1); // Error not sent to client } diff --git a/sql/select_handler.h b/sql/select_handler.h index e2ad13b7cdf..5cc63231641 100644 --- a/sql/select_handler.h +++ b/sql/select_handler.h @@ -41,12 +41,24 @@ class select_handler The table is actually never filled. Only its record buffer is used. */ TABLE *table; + List<Item> result_columns; - select_handler(THD *thd_arg, handlerton *ht_arg) - : thd(thd_arg), ht(ht_arg), table(0) {} + bool is_analyze; - virtual ~select_handler() {} + bool send_result_set_metadata(); + bool send_data(); + select_handler(THD *thd_arg, handlerton *ht_arg); + + virtual ~select_handler(); + + int execute(); + + virtual bool prepare(); + + static TABLE *create_tmp_table(THD *thd, SELECT_LEX *sel); + +protected: /* Functions to scan the select result set. All these returns 0 if ok, error code in case of error. @@ -67,6 +79,8 @@ class select_handler /* Report errors */ virtual void print_error(int error, myf errflag); + + bool send_eof(); }; #endif /* SELECT_HANDLER_INCLUDED */ diff --git a/sql/session_tracker.cc b/sql/session_tracker.cc index 1e5e356a5ba..de82d8be90c 100644 --- a/sql/session_tracker.cc +++ b/sql/session_tracker.cc @@ -445,8 +445,11 @@ bool Session_sysvars_tracker::vars_list::store(THD *thd, String *buf) show.name= svar->name.str; show.value= (char *) svar; + mysql_mutex_lock(&LOCK_global_system_variables); const char *value= get_one_variable(thd, &show, OPT_SESSION, SHOW_SYS, NULL, &charset, val_buf, &val_length); + mysql_mutex_unlock(&LOCK_global_system_variables); + if (is_plugin) mysql_mutex_unlock(&LOCK_plugin); diff --git a/sql/set_var.cc b/sql/set_var.cc index 38c8d1f1775..fe98d98fbd4 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -1084,7 +1084,6 @@ static void store_var(Field *field, sys_var *var, enum_var_type scope, int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond) { char name_buffer[NAME_CHAR_LEN]; - enum_check_fields save_count_cuted_fields= thd->count_cuted_fields; bool res= 1; CHARSET_INFO *scs= system_charset_info; StringBuffer<STRING_BUFFER_USUAL_SIZE> strbuf(scs); @@ -1095,7 +1094,6 @@ int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond) DBUG_ASSERT(tables->table->in_use == thd); cond= make_cond_for_info_schema(thd, cond, tables); - thd->count_cuted_fields= CHECK_FIELD_WARN; mysql_prlock_rdlock(&LOCK_system_variables_hash); for (uint i= 0; i < system_variable_hash.records; i++) @@ -1269,7 +1267,6 @@ int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond) res= 0; end: mysql_prlock_unlock(&LOCK_system_variables_hash); - thd->count_cuted_fields= save_count_cuted_fields; return res; } diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 63c3ee30444..2e08892166d 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -1800,9 +1800,10 @@ ER_WRONG_AUTO_KEY 42000 S1009 slo "Môžete mať iba jedno AUTO pole a to musí byť definované ako kľúč" spa "Puede ser solamente un campo automatico y este debe ser definido como una clave" swe "Det får finnas endast ett AUTO_INCREMENT-fält och detta måste vara en nyckel" - ukr "Невірне визначення таблиці; Може бути лише один автоматичний стовбець, що повинен бути визначений як ключ" + ukr "Хибне визначення таблиці; Може бути лише один автоматичний стовбець, що повинен бути визначений як ключ" ER_BINLOG_CANT_DELETE_GTID_DOMAIN eng "Could not delete gtid domain. Reason: %s." + ukr "Не можу видалити домен gtid. Причина: %s." ER_NORMAL_SHUTDOWN cze "%s (%s): normální ukončení" dan "%s (%s): Normal nedlukning" @@ -2203,6 +2204,7 @@ ER_INSERT_INFO ER_UPDATE_TABLE_USED eng "Table '%-.192s' is specified twice, both as a target for '%s' and as a separate source for data" swe "Table '%-.192s' är använd två gånger. Både för '%s' och för att hämta data" + ukr "Таблиця '%-.192s' вказується двічі, як цільова для '%s', так і як окреме джерело даних" ER_NO_SUCH_THREAD cze "Neznámá identifikace threadu: %lu" dan "Ukendt tråd id: %lu" @@ -2824,6 +2826,7 @@ ER_WRONG_OUTER_JOIN 42000 ER_NULL_COLUMN_IN_INDEX 42000 eng "Table handler doesn't support NULL in given index. Please change column '%-.192s' to be NOT NULL or use another handler" swe "Tabell hanteraren kan inte indexera NULL kolumner för den givna index typen. Ändra '%-.192s' till NOT NULL eller använd en annan hanterare" + ukr "Вказівник таблиці не підтримує NULL у зазначенному індексі. Будь ласка, зменіть стовпчик '%-.192s' на NOT NULL або використайте інший вказівник таблиці." ER_CANT_FIND_UDF cze "Nemohu načíst funkci '%-.192s'" dan "Kan ikke læse funktionen '%-.192s'" @@ -4314,6 +4317,7 @@ ER_MASTER_INFO jpn "'master info '%.*s''構造体の初期化ができませんでした。MariaDBエラーログでエラーメッセージを確認してください。" serbian "Nisam mogao da inicijalizujem informacionu strukturu glavnog servera, proverite da li imam privilegije potrebne za pristup file-u 'master.info' '%.*s'" swe "Kunde inte initialisera replikationsstrukturerna för '%.*s'. See MariaDB fel fil för mera information" + ukr "Інформаційна структура з'єднання головного і підлеглого (master.info) для '%.*s' не може бути ініціалізована" ER_SLAVE_THREAD dan "Kunne ikke danne en slave-tråd; check systemressourcerne" nla "Kon slave thread niet aanmaken, controleer systeem resources" @@ -4478,6 +4482,7 @@ ER_UNION_TABLES_IN_DIFFERENT_DIR serbian "Pogrešna definicija tabele; sve 'MERGE' tabele moraju biti u istoj bazi podataka" spa "Incorrecta definición de la tabla; Todas las tablas MERGE deben estar en el mismo banco de datos" swe "Felaktig tabelldefinition; alla tabeller i en MERGE-tabell måste vara i samma databas" + ukr "Хибне визначення таблиці; всі MERGE-таблиці повинні належити до однієї бази ланних." ER_LOCK_DEADLOCK 40001 nla "Deadlock gevonden tijdens lock-aanvraag poging; Probeer herstart van de transactie" eng "Deadlock found when trying to get lock; try restarting transaction" @@ -4491,6 +4496,7 @@ ER_LOCK_DEADLOCK 40001 serbian "Unakrsno zaključavanje pronađeno kada sam pokušao da dobijem pravo na zaključavanje; Probajte da restartujete transakciju" spa "Encontrado deadlock cuando tentando obtener el bloqueo; Tente recomenzar la transición" swe "Fick 'DEADLOCK' vid låsförsök av block/rad. Försök att starta om transaktionen" + ukr "Взаємне блокування знайдено під час спроби отримати блокування; спробуйте перезапустити транзакцію." ER_TABLE_CANT_HANDLE_FT nla "Het gebruikte tabel type (%s) ondersteund geen FULLTEXT indexen" eng "The storage engine %s doesn't support FULLTEXT indexes" @@ -4517,6 +4523,7 @@ ER_CANNOT_ADD_FOREIGN serbian "Ne mogu da dodam proveru spoljnog ključa na `%s`" spa "No puede adicionar clave extranjera constraint para `%s`" swe "Kan inte lägga till 'FOREIGN KEY constraint' för `%s`'" + ukr "Не можу додати обмеження зовнішнього ключа Ha `%s`" ER_NO_REFERENCED_ROW 23000 nla "Kan onderliggende rij niet toevoegen: foreign key beperking gefaald" eng "Cannot add or update a child row: a foreign key constraint fails" @@ -4531,6 +4538,7 @@ ER_NO_REFERENCED_ROW 23000 rus "Невозможно добавить или обновить дочернюю строку: проверка ограничений внешнего ключа не выполняется" spa "No puede adicionar una línea hijo: falla de clave extranjera constraint" swe "FOREIGN KEY-konflikt: Kan inte skriva barn" + ukr "Не вдається додати або оновити дочірній рядок: невдала перевірка обмеження зовнішнього ключа" ER_ROW_IS_REFERENCED 23000 eng "Cannot delete or update a parent row: a foreign key constraint fails" fre "Impossible de supprimer un enregistrement père : une constrainte externe l'empèche" @@ -5714,8 +5722,9 @@ ER_SP_RECURSION_LIMIT eng "Recursive limit %d (as set by the max_sp_recursion_depth variable) was exceeded for routine %.192s" ger "Rekursionsgrenze %d (durch Variable max_sp_recursion_depth gegeben) wurde für Routine %.192s überschritten" ER_SP_PROC_TABLE_CORRUPT - eng "Failed to load routine %-.192s. The table mysql.proc is missing, corrupt, or contains bad data (internal code %d)" - ger "Routine %-.192s konnte nicht geladen werden. Die Tabelle mysql.proc fehlt, ist beschädigt, oder enthält fehlerhaften Daten (interner Code: %d)" + eng "Failed to load routine %-.192s (internal code %d). For more details, run SHOW WARNINGS" + ger "Routine %-.192s (interner Code %d) konnte nicht geladen werden. Weitere Einzelheiten erhalten Sie, wenn Sie SHOW WARNINGS ausführen" + ukr "Невдала спроба завантажити процедуру %-.192s (внутрішний код %d). Для отримання детальної інформації використовуйте SHOW WARNINGS" ER_SP_WRONG_NAME 42000 eng "Incorrect routine name '%-.192s'" ger "Ungültiger Routinenname '%-.192s'" diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 49c8229c277..9e5b5bee0f2 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1211,6 +1211,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success) Query_arena execute_arena(&execute_mem_root, STMT_INITIALIZED_FOR_SP), backup_arena; query_id_t old_query_id; + CSET_STRING old_query; TABLE *old_derived_tables; TABLE *old_rec_tables; LEX *old_lex; @@ -1291,6 +1292,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success) be able properly do close_thread_tables() in instructions. */ old_query_id= thd->query_id; + old_query= thd->query_string; old_derived_tables= thd->derived_tables; thd->derived_tables= 0; old_rec_tables= thd->rec_tables; @@ -1567,6 +1569,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success) old_change_list.move_elements_to(thd); thd->lex= old_lex; thd->set_query_id(old_query_id); + thd->set_query_inner(old_query); DBUG_ASSERT(!thd->derived_tables); thd->derived_tables= old_derived_tables; thd->rec_tables= old_rec_tables; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 161ffc66641..2c96e0e9ff2 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -304,8 +304,9 @@ ulong role_global_merges= 0, role_db_merges= 0, role_table_merges= 0, static void update_hostname(acl_host_and_ip *host, const char *hostname); static bool show_proxy_grants (THD *, const char *, const char *, char *, size_t); -static bool show_role_grants(THD *, const char *, const char *, +static bool show_role_grants(THD *, const char *, ACL_USER_BASE *, char *, size_t); +static bool show_default_role(THD *, ACL_USER *, char *, size_t); static bool show_global_privileges(THD *, ACL_USER_BASE *, bool, char *, size_t); static bool show_database_privileges(THD *, const char *, const char *, @@ -9000,7 +9001,7 @@ static bool print_grants_for_role(THD *thd, ACL_ROLE * role) { char buff[1024]; - if (show_role_grants(thd, role->user.str, "", role, buff, sizeof(buff))) + if (show_role_grants(thd, "", role, buff, sizeof(buff))) return TRUE; if (show_global_privileges(thd, role, TRUE, buff, sizeof(buff))) @@ -9245,7 +9246,7 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user) } /* Show granted roles to acl_user */ - if (show_role_grants(thd, username, hostname, acl_user, buff, sizeof(buff))) + if (show_role_grants(thd, hostname, acl_user, buff, sizeof(buff))) goto end; /* Add first global access grants */ @@ -9302,6 +9303,14 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user) } } + if (username) + { + /* Show default role to acl_user */ + if (show_default_role(thd, acl_user, buff, sizeof(buff))) + goto end; + } + + error= 0; end: mysql_mutex_unlock(&acl_cache->lock); @@ -9328,15 +9337,44 @@ static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u, my_hash_search(&acl_roles_mappings, (uchar*)pair_key.ptr(), key_length); } -static bool show_role_grants(THD *thd, const char *username, - const char *hostname, ACL_USER_BASE *acl_entry, +static bool show_default_role(THD *thd, ACL_USER *acl_entry, + char *buff, size_t buffsize) +{ + Protocol *protocol= thd->protocol; + LEX_CSTRING def_rolename= acl_entry->default_rolename; + + if (def_rolename.length) + { + String def_str(buff, buffsize, system_charset_info); + def_str.length(0); + def_str.append(STRING_WITH_LEN("SET DEFAULT ROLE ")); + def_str.append(&def_rolename); + def_str.append(" FOR '"); + def_str.append(&acl_entry->user); + DBUG_ASSERT(!(acl_entry->flags & IS_ROLE)); + def_str.append(STRING_WITH_LEN("'@'")); + def_str.append(acl_entry->host.hostname, acl_entry->hostname_length, + system_charset_info); + def_str.append('\''); + protocol->prepare_for_resend(); + protocol->store(def_str.ptr(),def_str.length(),def_str.charset()); + if (protocol->write()) + { + return TRUE; + } + } + return FALSE; +} + +static bool show_role_grants(THD *thd, const char *hostname, + ACL_USER_BASE *acl_entry, char *buff, size_t buffsize) { uint counter; Protocol *protocol= thd->protocol; LEX_CSTRING host= {const_cast<char*>(hostname), strlen(hostname)}; - String grant(buff,sizeof(buff),system_charset_info); + String grant(buff, buffsize, system_charset_info); for (counter= 0; counter < acl_entry->role_grants.elements; counter++) { grant.length(0); @@ -9377,7 +9415,7 @@ static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry, privilege_t want_access(NO_ACL); Protocol *protocol= thd->protocol; - String global(buff,sizeof(buff),system_charset_info); + String global(buff, buffsize, system_charset_info); global.length(0); global.append(STRING_WITH_LEN("GRANT ")); @@ -9471,7 +9509,7 @@ static bool show_database_privileges(THD *thd, const char *username, want_access=acl_db->initial_access; if (want_access) { - String db(buff,sizeof(buff),system_charset_info); + String db(buff, buffsize, system_charset_info); db.length(0); db.append(STRING_WITH_LEN("GRANT ")); @@ -14679,3 +14717,40 @@ maria_declare_plugin(mysql_password) MariaDB_PLUGIN_MATURITY_STABLE /* Maturity */ } maria_declare_plugin_end; + + +/* + Exporting functions that allow plugins to do server-style + host/user matching. Used in server_audit2 plugin. +*/ +extern "C" int maria_compare_hostname( + const char *wild_host, long wild_ip, long ip_mask, + const char *host, const char *ip) +{ +#ifndef NO_EMBEDDED_ACCESS_CHECKS + acl_host_and_ip h; + h.hostname= (char *) wild_host; + h.ip= wild_ip; + h.ip_mask= ip_mask; + + return compare_hostname(&h, host, ip); +#else + return 0; +#endif +} + + +extern "C" void maria_update_hostname( + const char **wild_host, long *wild_ip, long *ip_mask, + const char *host) +{ +#ifndef NO_EMBEDDED_ACCESS_CHECKS + acl_host_and_ip h; + update_hostname(&h, host); + *wild_host= h.hostname; + *wild_ip= h.ip; + *ip_mask= h.ip_mask; +#endif +} + + diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 6a313616025..dec5bd91f36 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1316,7 +1316,12 @@ bool wait_while_table_is_used(THD *thd, TABLE *table, table->s->tdc->flush(thd, true); /* extra() call must come only after all instances above are closed */ if (function != HA_EXTRA_NOT_USED) - DBUG_RETURN(table->file->extra(function)); + { + int error= table->file->extra(function); + if (error) + table->file->print_error(error, MYF(0)); + DBUG_RETURN(error); + } DBUG_RETURN(FALSE); } @@ -1777,7 +1782,14 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx) } if (is_locked_view(thd, table_list)) + { + if (table_list->sequence) + { + my_error(ER_NOT_SEQUENCE, MYF(0), table_list->db.str, table_list->alias.str); + DBUG_RETURN(true); + } DBUG_RETURN(FALSE); // VIEW + } /* No table in the locked tables list. In case of explicit LOCK TABLES @@ -1906,7 +1918,12 @@ retry_share: DBUG_RETURN(FALSE); } +#ifdef WITH_WSREP + if (!((flags & MYSQL_OPEN_IGNORE_FLUSH) || + (thd->wsrep_applier))) +#else if (!(flags & MYSQL_OPEN_IGNORE_FLUSH)) +#endif { if (share->tdc->flushed) { @@ -3900,7 +3917,8 @@ static bool upgrade_lock_if_not_exists(THD *thd, { DEBUG_SYNC(thd,"create_table_before_check_if_exists"); if (!create_info.or_replace() && - ha_table_exists(thd, &create_table->db, &create_table->table_name)) + ha_table_exists(thd, &create_table->db, &create_table->table_name, + &create_table->db_type)) { if (create_info.if_not_exists()) { @@ -3948,7 +3966,8 @@ static bool upgrade_lock_if_not_exists(THD *thd, Note that for CREATE TABLE IF EXISTS we only generate a warning but still return TRUE (to abort the calling open_table() function). On must check THD->is_error() if one wants to distinguish between warning - and error. + and error. If table existed, tables_start->db_type is set to the handlerton + for the found table. */ bool @@ -8734,14 +8753,17 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values, goto err; field->set_has_explicit_value(); } - /* Update virtual fields */ - thd->abort_on_warning= FALSE; - if (table->versioned()) - table->vers_update_fields(); - if (table->vfield && - table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_WRITE)) - goto err; - thd->abort_on_warning= abort_on_warning_saved; + /* Update virtual fields if there wasn't any errors */ + if (!thd->is_error()) + { + thd->abort_on_warning= FALSE; + if (table->versioned()) + table->vers_update_fields(); + if (table->vfield && + table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_WRITE)) + goto err; + thd->abort_on_warning= abort_on_warning_saved; + } DBUG_RETURN(thd->is_error()); err: diff --git a/sql/sql_class.cc b/sql/sql_class.cc index e32344115da..3db7ebc7997 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1298,6 +1298,7 @@ void THD::init() first_successful_insert_id_in_prev_stmt_for_binlog= 0; first_successful_insert_id_in_cur_stmt= 0; current_backup_stage= BACKUP_FINISHED; + backup_commit_lock= 0; #ifdef WITH_WSREP wsrep_last_query_id= 0; wsrep_xid.null(); @@ -2588,7 +2589,7 @@ void THD::give_protection_error() my_error(ER_BACKUP_LOCK_IS_ACTIVE, MYF(0)); else { - DBUG_ASSERT(global_read_lock.is_acquired()); + DBUG_ASSERT(global_read_lock.is_acquired() || mdl_backup_lock); my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0)); } } @@ -6552,8 +6553,8 @@ exit:; /** Check if we should log a table DDL to the binlog - @@return true yes - @@return false no + @retval true yes + @retval false no */ bool THD::binlog_table_should_be_logged(const LEX_CSTRING *db) @@ -7442,14 +7443,14 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg, */ if (binlog_should_compress(query_len)) { - Query_compressed_log_event qinfo(this, query_arg, query_len, is_trans, direct, - suppress_use, errcode); + Query_compressed_log_event qinfo(this, query_arg, query_len, is_trans, + direct, suppress_use, errcode); error= mysql_bin_log.write(&qinfo); } else { Query_log_event qinfo(this, query_arg, query_len, is_trans, direct, - suppress_use, errcode); + suppress_use, errcode); error= mysql_bin_log.write(&qinfo); } /* @@ -7467,6 +7468,38 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg, DBUG_RETURN(0); } + +/** + Binlog current query as a statement, ignoring the binlog filter setting. + + The filter is in decide_logging_format() to mark queries to not be stored + in the binary log, for example by a shared distributed engine like S3. + This function resets the filter to ensure the the query is logged if + the binlog is active. + + Note that 'direct' is set to false, which means that the query will + not be directly written to the binary log but instead to the cache. + + @retval false ok + @retval true error +*/ + + +bool THD::binlog_current_query_unfiltered() +{ + if (!mysql_bin_log.is_open()) + return 0; + + reset_binlog_local_stmt_filter(); + clear_binlog_local_stmt_filter(); + return binlog_query(THD::STMT_QUERY_TYPE, query(), query_length(), + /* is_trans */ FALSE, + /* direct */ FALSE, + /* suppress_use */ FALSE, + /* Error */ 0) > 0; +} + + void THD::wait_for_wakeup_ready() { @@ -7642,16 +7675,33 @@ wait_for_commit::register_wait_for_prior_commit(wait_for_commit *waitee) } -/* - Wait for commit of another transaction to complete, as already registered +/** + Waits for commit of another transaction to complete, as already registered with register_wait_for_prior_commit(). If the commit already completed, returns immediately. + + If thd->backup_commit_lock is set, release it while waiting for other threads */ + int wait_for_commit::wait_for_prior_commit2(THD *thd) { PSI_stage_info old_stage; wait_for_commit *loc_waitee; + bool backup_lock_released= 0; + + /* + Release MDL_BACKUP_COMMIT LOCK while waiting for other threads to commit + This is needed to avoid deadlock between the other threads (which not + yet have the MDL_BACKUP_COMMIT_LOCK) and any threads using + BACKUP LOCK BLOCK_COMMIT. + */ + if (thd->backup_commit_lock && thd->backup_commit_lock->ticket) + { + backup_lock_released= 1; + thd->mdl_context.release_lock(thd->backup_commit_lock->ticket); + thd->backup_commit_lock->ticket= 0; + } mysql_mutex_lock(&LOCK_wait_commit); DEBUG_SYNC(thd, "wait_for_prior_commit_waiting"); @@ -7701,10 +7751,16 @@ wait_for_commit::wait_for_prior_commit2(THD *thd) use within enter_cond/exit_cond. */ DEBUG_SYNC(thd, "wait_for_prior_commit_killed"); + if (backup_lock_released) + thd->mdl_context.acquire_lock(thd->backup_commit_lock, + thd->variables.lock_wait_timeout); return wakeup_error; end: thd->EXIT_COND(&old_stage); + if (backup_lock_released) + thd->mdl_context.acquire_lock(thd->backup_commit_lock, + thd->variables.lock_wait_timeout); return wakeup_error; } diff --git a/sql/sql_class.h b/sql/sql_class.h index b0f68aa8fab..f6b772ef439 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -284,8 +284,9 @@ class Key_part_spec :public Sql_alloc { public: LEX_CSTRING field_name; uint length; - Key_part_spec(const LEX_CSTRING *name, uint len) - : field_name(*name), length(len) + bool generated; + Key_part_spec(const LEX_CSTRING *name, uint len, bool gen= false) + : field_name(*name), length(len), generated(gen) {} bool operator==(const Key_part_spec& other) const; /** @@ -2311,7 +2312,10 @@ public: rpl_io_thread_info *rpl_io_info; rpl_sql_thread_info *rpl_sql_info; } system_thread_info; + /* Used for BACKUP LOCK */ MDL_ticket *mdl_backup_ticket, *mdl_backup_lock; + /* Used to register that thread has a MDL_BACKUP_WAIT_COMMIT lock */ + MDL_request *backup_commit_lock; void reset_for_next_command(bool do_clear_errors= 1); /* @@ -2758,10 +2762,14 @@ public: { free_root(&mem_root,MYF(0)); } - my_bool is_active() + bool is_active() { return (all.ha_list != NULL); } + bool is_empty() + { + return all.is_empty() && stmt.is_empty(); + } st_transactions() { bzero((char*)this, sizeof(*this)); @@ -3483,6 +3491,7 @@ public: char const *query, ulong query_len, bool is_trans, bool direct, bool suppress_use, int errcode); + bool binlog_current_query_unfiltered(); #endif inline void @@ -3765,10 +3774,17 @@ public: /* Commit both statement and full transaction */ int commit_whole_transaction_and_close_tables(); void give_protection_error(); + /* + Give an error if any of the following is true for this connection + - BACKUP STAGE is active + - FLUSH TABLE WITH READ LOCK is active + - BACKUP LOCK table_name is active + */ inline bool has_read_only_protection() { if (current_backup_stage == BACKUP_FINISHED && - !global_read_lock.is_acquired()) + !global_read_lock.is_acquired() && + !mdl_backup_lock) return FALSE; give_protection_error(); return TRUE; @@ -6873,11 +6889,11 @@ public: /** SP Bulk execution safe */ -#define CF_SP_BULK_SAFE (1U << 20) +#define CF_PS_ARRAY_BINDING_SAFE (1U << 20) /** SP Bulk execution optimized */ -#define CF_SP_BULK_OPTIMIZED (1U << 21) +#define CF_PS_ARRAY_BINDING_OPTIMIZED (1U << 21) /** If command creates or drops a table */ diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 17f8995239c..35aa306c88c 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -248,6 +248,7 @@ int update_portion_of_time(THD *thd, TABLE *table, uint dst_fieldno= lcond ? table->s->period.end_fieldno : table->s->period.start_fieldno; + table->file->store_auto_increment(); store_record(table, record[1]); if (likely(!res)) res= src->save_in_field(table->field[dst_fieldno], true); @@ -262,6 +263,8 @@ int update_portion_of_time(THD *thd, TABLE *table, res= table->triggers->process_triggers(thd, TRG_EVENT_INSERT, TRG_ACTION_AFTER, true); restore_record(table, record[1]); + if (res) + table->file->restore_auto_increment(); if (likely(!res) && lcond && rcond) res= table->period_make_insert(period_conds.end.item, diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 4ad4c478937..7833059438e 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -724,6 +724,7 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list, Item *unused_conds= 0; DBUG_ENTER("mysql_insert"); + bzero((char*) &info,sizeof(info)); create_explain_query(thd->lex, thd->mem_root); /* Upgrade lock type if the requested lock is incompatible with @@ -764,16 +765,28 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list, DBUG_RETURN(TRUE); value_count= values->elements; - if (mysql_prepare_insert(thd, table_list, table, fields, values, - update_fields, update_values, duplic, - &unused_conds, FALSE)) + if ((res= mysql_prepare_insert(thd, table_list, fields, values, + update_fields, update_values, duplic, + &unused_conds, FALSE))) + { + retval= thd->is_error(); + if (res < 0) + { + /* + Insert should be ignored but we have to log the query in statement + format in the binary log + */ + if (thd->binlog_current_query_unfiltered()) + retval= 1; + } goto abort; + } + /* mysql_prepare_insert sets table_list->table if it was not set */ + table= table_list->table; /* Prepares LEX::returing_list if it is not empty */ if (returning) result->prepare(returning->item_list, NULL); - /* mysql_prepare_insert sets table_list->table if it was not set */ - table= table_list->table; context= &thd->lex->first_select_lex()->context; /* @@ -828,7 +841,6 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list, /* Fill in the given fields and dump it to the table file */ - bzero((char*) &info,sizeof(info)); info.ignore= ignore; info.handle_duplicates=duplic; info.update_fields= &update_fields; @@ -1199,7 +1211,7 @@ values_loop_end: such case the flag is ignored for constructing binlog event. */ DBUG_ASSERT(thd->killed != KILL_BAD_DATA || error > 0); - if (was_insert_delayed && table_list->lock_type == TL_WRITE) + if (was_insert_delayed && table_list->lock_type == TL_WRITE) { /* Binlog INSERT DELAYED as INSERT without DELAYED. */ String log_query; @@ -1534,9 +1546,6 @@ static void prepare_for_positional_update(TABLE *table, TABLE_LIST *tables) mysql_prepare_insert() thd Thread handler table_list Global/local table list - table Table to insert into - (can be NULL if table should - be taken from table_list->table) where Where clause (for insert ... select) select_insert TRUE if INSERT ... SELECT statement @@ -1551,15 +1560,16 @@ static void prepare_for_positional_update(TABLE *table, TABLE_LIST *tables) before releasing the table object. RETURN VALUE - FALSE OK - TRUE error + 0 OK + >0 error + <0 insert should be ignored */ -bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, - TABLE *table, List<Item> &fields, List_item *values, - List<Item> &update_fields, List<Item> &update_values, - enum_duplicates duplic, COND **where, - bool select_insert) +int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, + List<Item> &fields, List_item *values, + List<Item> &update_fields, List<Item> &update_values, + enum_duplicates duplic, COND **where, + bool select_insert) { SELECT_LEX *select_lex= thd->lex->first_select_lex(); Name_resolution_context *context= &select_lex->context; @@ -1567,29 +1577,34 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, bool insert_into_view= (table_list->view != 0); bool res= 0; table_map map= 0; + TABLE *table; DBUG_ENTER("mysql_prepare_insert"); - DBUG_PRINT("enter", ("table_list: %p table: %p view: %d", - table_list, table, - (int)insert_into_view)); + DBUG_PRINT("enter", ("table_list: %p view: %d", + table_list, (int) insert_into_view)); /* INSERT should have a SELECT or VALUES clause */ DBUG_ASSERT (!select_insert || !values); if (mysql_handle_derived(thd->lex, DT_INIT)) - DBUG_RETURN(TRUE); + DBUG_RETURN(1); if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) - DBUG_RETURN(TRUE); + DBUG_RETURN(1); if (thd->lex->handle_list_of_derived(table_list, DT_PREPARE)) - DBUG_RETURN(TRUE); + DBUG_RETURN(1); if (duplic == DUP_UPDATE) { /* it should be allocated before Item::fix_fields() */ if (table_list->set_insert_values(thd->mem_root)) - DBUG_RETURN(TRUE); + DBUG_RETURN(1); } + table= table_list->table; + + if (table->file->check_if_updates_are_ignored("INSERT")) + DBUG_RETURN(-1); + if (mysql_prepare_insert_check_table(thd, table_list, fields, select_insert)) - DBUG_RETURN(TRUE); + DBUG_RETURN(1); /* Prepare the fields in the statement. */ if (values) @@ -1632,9 +1647,6 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, if (res) DBUG_RETURN(res); - if (!table) - table= table_list->table; - if (check_duplic_insert_without_overlaps(thd, table, duplic) != 0) DBUG_RETURN(true); @@ -1642,7 +1654,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, { // Additional memory may be required to create historical items. if (table_list->set_insert_values(thd->mem_root)) - DBUG_RETURN(TRUE); + DBUG_RETURN(1); } if (!select_insert) @@ -1653,7 +1665,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, CHECK_DUP_ALLOW_DIFFERENT_ALIAS))) { update_non_unique_table_error(table_list, "INSERT", duplicate); - DBUG_RETURN(TRUE); + DBUG_RETURN(1); } select_lex->fix_prepare_information(thd, &fake_conds, &fake_conds); } @@ -1663,7 +1675,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, */ if (duplic == DUP_UPDATE || duplic == DUP_REPLACE) prepare_for_positional_update(table, table_list); - DBUG_RETURN(FALSE); + DBUG_RETURN(0); } @@ -3703,12 +3715,14 @@ bool Delayed_insert::handle_inserts(void) thd thread handler RETURN - FALSE OK - TRUE Error + 0 OK + > 0 Error + < 0 Ok, ignore insert */ -bool mysql_insert_select_prepare(THD *thd, select_result *sel_res) +int mysql_insert_select_prepare(THD *thd, select_result *sel_res) { + int res; LEX *lex= thd->lex; SELECT_LEX *select_lex= lex->first_select_lex(); DBUG_ENTER("mysql_insert_select_prepare"); @@ -3718,11 +3732,11 @@ bool mysql_insert_select_prepare(THD *thd, select_result *sel_res) clause if table is VIEW */ - if (mysql_prepare_insert(thd, lex->query_tables, - lex->query_tables->table, lex->field_list, 0, - lex->update_list, lex->value_list, lex->duplicates, - &select_lex->where, TRUE)) - DBUG_RETURN(TRUE); + if ((res= mysql_prepare_insert(thd, lex->query_tables, lex->field_list, 0, + lex->update_list, lex->value_list, + lex->duplicates, + &select_lex->where, TRUE))) + DBUG_RETURN(res); /* If sel_res is not empty, it means we have items in returing_list. @@ -3763,7 +3777,7 @@ bool mysql_insert_select_prepare(THD *thd, select_result *sel_res) while ((table= ti++) && insert_tables--) ti.remove(); - DBUG_RETURN(FALSE); + DBUG_RETURN(0); } @@ -3809,15 +3823,18 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) */ lex->current_select= lex->first_select_lex(); - res= setup_returning_fields(thd, table_list) || - setup_fields(thd, Ref_ptr_array(), values, MARK_COLUMNS_READ, 0, 0, 0) || - check_insert_fields(thd, table_list, *fields, values, - !insert_into_view, 1, &map); + res= (setup_returning_fields(thd, table_list) || + setup_fields(thd, Ref_ptr_array(), values, MARK_COLUMNS_READ, 0, 0, + 0) || + check_insert_fields(thd, table_list, *fields, values, + !insert_into_view, 1, &map)); if (!res && fields->elements) { - Abort_on_warning_instant_set aws(thd, !info.ignore && thd->is_strict_mode()); - res= check_that_all_fields_are_given_values(thd, table_list->table, table_list); + Abort_on_warning_instant_set aws(thd, + !info.ignore && thd->is_strict_mode()); + res= check_that_all_fields_are_given_values(thd, table_list->table, + table_list); } if (info.handle_duplicates == DUP_UPDATE && !res) diff --git a/sql/sql_insert.h b/sql/sql_insert.h index 14041976973..80666a81c50 100644 --- a/sql/sql_insert.h +++ b/sql/sql_insert.h @@ -23,11 +23,11 @@ typedef List<Item> List_item; typedef struct st_copy_info COPY_INFO; -bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table, - List<Item> &fields, List_item *values, - List<Item> &update_fields, - List<Item> &update_values, enum_duplicates duplic, - COND **where, bool select_insert); +int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, + List<Item> &fields, List_item *values, + List<Item> &update_fields, + List<Item> &update_values, enum_duplicates duplic, + COND **where, bool select_insert); bool mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields, List<List_item> &values, List<Item> &update_fields, List<Item> &update_values, enum_duplicates flag, diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 592b53ceecf..61e3b89c9a3 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3997,21 +3997,21 @@ bool LEX::can_not_use_merged() } } -/* - Detect that we need only table structure of derived table/view +/** + Detect that we need only table structure of derived table/view. - SYNOPSIS - only_view_structure() + Also used by I_S tables (@see create_schema_table) to detect that + they need a full table structure and cannot optimize unused columns away - RETURN - TRUE yes, we need only structure - FALSE no, we need data + @retval TRUE yes, we need only structure + @retval FALSE no, we need data */ bool LEX::only_view_structure() { switch (sql_command) { case SQLCOM_SHOW_CREATE: + case SQLCOM_CHECKSUM: case SQLCOM_SHOW_TABLES: case SQLCOM_SHOW_FIELDS: case SQLCOM_REVOKE_ALL: @@ -4019,6 +4019,8 @@ bool LEX::only_view_structure() case SQLCOM_GRANT: case SQLCOM_CREATE_VIEW: return TRUE; + case SQLCOM_CREATE_TABLE: + return create_info.like(); default: return FALSE; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index a07121b9a58..3e59ad3afc9 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1334,10 +1334,8 @@ public: table_value_constr *tvc; bool in_tvc; - /* The interface employed to execute the select query by a foreign engine */ - select_handler *select_h; /* The object used to organize execution of the query by a foreign engine */ - Pushdown_select *pushdown_select; + select_handler *pushdown_select; /** System Versioning */ public: diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 554c62f6538..0488b240bb2 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -98,6 +98,8 @@ #include "my_json_writer.h" +#define PRIV_LOCK_TABLES (SELECT_ACL | LOCK_TABLES_ACL) + #define FLAGSTR(V,F) ((V)&(F)?#F" ":"") #ifdef WITH_ARIA_STORAGE_ENGINE @@ -576,19 +578,21 @@ void init_update_queries(void) CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | CF_CAN_BE_EXPLAINED | - CF_UPDATES_DATA | CF_SP_BULK_SAFE; + CF_UPDATES_DATA | + CF_PS_ARRAY_BINDING_SAFE; sql_command_flags[SQLCOM_UPDATE_MULTI]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | CF_CAN_BE_EXPLAINED | - CF_UPDATES_DATA | CF_SP_BULK_SAFE; + CF_UPDATES_DATA | + CF_PS_ARRAY_BINDING_SAFE; sql_command_flags[SQLCOM_INSERT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | CF_CAN_BE_EXPLAINED | CF_INSERTS_DATA | - CF_SP_BULK_SAFE | - CF_SP_BULK_OPTIMIZED; + CF_PS_ARRAY_BINDING_SAFE | + CF_PS_ARRAY_BINDING_OPTIMIZED; sql_command_flags[SQLCOM_INSERT_SELECT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | @@ -598,7 +602,8 @@ void init_update_queries(void) CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | CF_CAN_BE_EXPLAINED | - CF_SP_BULK_SAFE | CF_DELETES_DATA; + CF_DELETES_DATA | + CF_PS_ARRAY_BINDING_SAFE; sql_command_flags[SQLCOM_DELETE_MULTI]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | @@ -608,8 +613,9 @@ void init_update_queries(void) CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | CF_CAN_BE_EXPLAINED | - CF_INSERTS_DATA | CF_SP_BULK_SAFE | - CF_SP_BULK_OPTIMIZED; + CF_INSERTS_DATA | + CF_PS_ARRAY_BINDING_SAFE | + CF_PS_ARRAY_BINDING_OPTIMIZED; sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | @@ -772,7 +778,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_ALTER_SERVER]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DROP_SERVER]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_BACKUP]= CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_BACKUP_LOCK]= 0; + sql_command_flags[SQLCOM_BACKUP_LOCK]= CF_AUTO_COMMIT_TRANS; /* The following statements can deal with temporary tables, @@ -1090,7 +1096,6 @@ int bootstrap(MYSQL_FILE *file) thd->reset_kill_query(); /* Ensure that killed_errmsg is released */ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); - thd->transaction->free(); thd->lex->restore_set_statement_var(); } delete thd; @@ -2825,6 +2830,36 @@ retry: goto err; } } + /* + Check privileges of view tables here, after views were opened. + Either definer or invoker has to have PRIV_LOCK_TABLES to be able + to lock view and its tables. For mysqldump (that locks views + before dumping their structures) compatibility we allow locking + views that select from I_S or P_S tables, but downrade the lock + to TL_READ + */ + if (table->belong_to_view && + check_single_table_access(thd, PRIV_LOCK_TABLES, table, 1)) + { + if (table->grant.m_internal.m_schema_access) + table->lock_type= TL_READ; + else + { + bool error= true; + if (Security_context *sctx= table->security_ctx) + { + table->security_ctx= 0; + error= check_single_table_access(thd, PRIV_LOCK_TABLES, table, 1); + table->security_ctx= sctx; + } + if (error) + { + my_error(ER_VIEW_INVALID, MYF(0), table->belong_to_view->view_db.str, + table->belong_to_view->view_name.str); + goto err; + } + } + } } if (lock_tables(thd, tables, counter, 0) || @@ -3683,6 +3718,7 @@ mysql_execute_command(THD *thd) lex->sql_command != SQLCOM_BEGIN && lex->sql_command != SQLCOM_CALL && lex->sql_command != SQLCOM_EXECUTE && + lex->sql_command != SQLCOM_EXECUTE_IMMEDIATE && !(sql_command_flags[lex->sql_command] & CF_AUTO_COMMIT_TRANS)) { wsrep_start_trx_if_not_started(thd); @@ -4255,6 +4291,11 @@ mysql_execute_command(THD *thd) /* mysql_update return 2 if we need to switch to multi-update */ if (up_result != 2) break; + if (thd->lex->period_conditions.is_set()) + { + DBUG_ASSERT(0); // Should never happen + goto error; + } } /* fall through */ case SQLCOM_UPDATE_MULTI: @@ -4569,6 +4610,14 @@ mysql_execute_command(THD *thd) } delete sel_result; } + else if (res < 0) + { + /* + Insert should be ignored but we have to log the query in statement + format in the binary log + */ + res= thd->binlog_current_query_unfiltered(); + } delete result; if (save_protocol) { @@ -4924,6 +4973,13 @@ mysql_execute_command(THD *thd) goto error; } + /* Should not lock tables while BACKUP LOCK is active */ + if (thd->mdl_backup_lock) + { + my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); + goto error; + } + /* Here we have to pre-open temporary tables for LOCK TABLES. @@ -5217,7 +5273,7 @@ mysql_execute_command(THD *thd) if (first_table && lex->type & (REFRESH_READ_LOCK|REFRESH_FOR_EXPORT)) { /* Check table-level privileges. */ - if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, + if (check_table_access(thd, PRIV_LOCK_TABLES, all_tables, FALSE, UINT_MAX, FALSE)) goto error; @@ -6328,24 +6384,21 @@ drop_routine(THD *thd, LEX *lex) ! lex->spname->m_explicit_name) { /* DROP FUNCTION <non qualified name> */ - udf_func *udf = find_udf(lex->spname->m_name.str, - lex->spname->m_name.length); - if (udf) - { - if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 0)) - return 1; - - if (!mysql_drop_function(thd, &lex->spname->m_name)) - { - my_ok(thd); - return 0; - } - my_error(ER_SP_DROP_FAILED, MYF(0), - "FUNCTION (UDF)", lex->spname->m_name.str); + enum drop_udf_result rc= mysql_drop_function(thd, &lex->spname->m_name); + switch (rc) { + case UDF_DEL_RESULT_DELETED: + my_ok(thd); + return 0; + case UDF_DEL_RESULT_ERROR: return 1; + case UDF_DEL_RESULT_ABSENT: + goto absent; } - if (lex->spname->m_db.str == NULL) + DBUG_ASSERT("wrong return code" == 0); +absent: + // If there was no current database, so it cannot be SP + if (!lex->spname->m_db.str) { if (lex->if_exists()) { @@ -6681,7 +6734,7 @@ check_access(THD *thd, privilege_t want_access, @param thd Thread handler @param privilege requested privilege - @param all_tables global table list of query + @param tables global table list of query @param no_errors FALSE/TRUE - report/don't report error to the client (using my_error() call). @@ -6691,28 +6744,25 @@ check_access(THD *thd, privilege_t want_access, 1 access denied, error is sent to client */ -bool check_single_table_access(THD *thd, privilege_t privilege, - TABLE_LIST *all_tables, bool no_errors) +bool check_single_table_access(THD *thd, privilege_t privilege, + TABLE_LIST *tables, bool no_errors) { - Switch_to_definer_security_ctx backup_sctx(thd, all_tables); + Switch_to_definer_security_ctx backup_sctx(thd, tables); const char *db_name; - if ((all_tables->view || all_tables->field_translation) && - !all_tables->schema_table) - db_name= all_tables->view_db.str; + if ((tables->view || tables->field_translation) && !tables->schema_table) + db_name= tables->view_db.str; else - db_name= all_tables->db.str; + db_name= tables->db.str; - if (check_access(thd, privilege, db_name, - &all_tables->grant.privilege, - &all_tables->grant.m_internal, - 0, no_errors)) + if (check_access(thd, privilege, db_name, &tables->grant.privilege, + &tables->grant.m_internal, 0, no_errors)) return 1; /* Show only 1 table for check_grant */ - if (!(all_tables->belong_to_view && - (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) && - check_grant(thd, privilege, all_tables, FALSE, 1, no_errors)) + if (!(tables->belong_to_view && + (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) && + check_grant(thd, privilege, tables, FALSE, 1, no_errors)) return 1; return 0; @@ -7755,7 +7805,6 @@ static bool wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state) { - int error __attribute__((unused)); DBUG_ENTER("mysql_parse"); DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on_MYSQLparse();); DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on_ORAparse();); @@ -7830,6 +7879,7 @@ void mysql_parse(THD *thd, char *rawbuf, uint length, (char *) thd->security_ctx->host_or_ip, 0); + int error __attribute__((unused)); error= mysql_execute_command(thd); MYSQL_QUERY_EXEC_DONE(error); } @@ -8928,8 +8978,6 @@ void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields, SELECT_LEX *lex) { b->natural_join= a; - a->part_of_natural_join= TRUE; - b->part_of_natural_join= TRUE; lex->prev_join_using= using_fields; } @@ -9795,7 +9843,7 @@ static bool lock_tables_precheck(THD *thd, TABLE_LIST *tables) if (is_temporary_table(table)) continue; - if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, table, + if (check_table_access(thd, PRIV_LOCK_TABLES, table, FALSE, 1, FALSE)) return TRUE; } diff --git a/sql/sql_parse.h b/sql/sql_parse.h index 36dc68c292c..37861cf224f 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -41,7 +41,7 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables); bool multi_delete_precheck(THD *thd, TABLE_LIST *tables); int mysql_multi_update_prepare(THD *thd); int mysql_multi_delete_prepare(THD *thd); -bool mysql_insert_select_prepare(THD *thd,select_result *sel_res); +int mysql_insert_select_prepare(THD *thd,select_result *sel_res); bool update_precheck(THD *thd, TABLE_LIST *tables); bool delete_precheck(THD *thd, TABLE_LIST *tables); bool insert_precheck(THD *thd, TABLE_LIST *tables); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 6753b857b54..57d6b27fcbb 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -124,6 +124,9 @@ When one supplies long data for a placeholder: #include "mysql/psi/mysql_ps.h" // MYSQL_EXECUTE_PS #include "wsrep_mysqld.h" +/* Constants defining bits in parameter type flags. Flags are read from high byte of short value */ +static const uint PARAMETER_FLAG_UNSIGNED = 128U << 8; + /** A result class used to send cursor rows using the binary protocol. */ @@ -897,11 +900,73 @@ static bool insert_bulk_params(Prepared_statement *stmt, DBUG_RETURN(0); } -static bool set_conversion_functions(Prepared_statement *stmt, - uchar **data, uchar *data_end) + +/** + Checking if parameter type and flags are valid + + @param typecode ushort value with type in low byte, and flags in high byte + + @retval true this parameter is wrong + @retval false this parameter is OK +*/ + +static bool +parameter_type_sanity_check(ushort typecode) +{ + /* Checking if type in lower byte is valid */ + switch (typecode & 0xff) { + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_NULL: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_GEOMETRY: + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_NEWDATE: + break; + /* + This types normally cannot be sent by client, so maybe it'd be + better to treat them like an error here. + */ + case MYSQL_TYPE_TIMESTAMP2: + case MYSQL_TYPE_TIME2: + case MYSQL_TYPE_DATETIME2: + default: + return true; + }; + + // In Flags in high byte only unsigned bit may be set + if (typecode & ((~PARAMETER_FLAG_UNSIGNED) & 0x0000ff00)) + { + return true; + } + return false; +} + +static bool +set_conversion_functions(Prepared_statement *stmt, uchar **data) { uchar *read_pos= *data; - const uint signed_bit= 1 << 15; + DBUG_ENTER("set_conversion_functions"); /* First execute or types altered by the client, setup the @@ -914,12 +979,17 @@ static bool set_conversion_functions(Prepared_statement *stmt, { ushort typecode; - if (read_pos >= data_end) - DBUG_RETURN(1); - + /* + stmt_execute_packet_sanity_check has already verified, that there + are enough data in the packet for data types + */ typecode= sint2korr(read_pos); read_pos+= 2; - (**it).unsigned_flag= MY_TEST(typecode & signed_bit); + if (parameter_type_sanity_check(typecode)) + { + DBUG_RETURN(1); + } + (**it).unsigned_flag= MY_TEST(typecode & PARAMETER_FLAG_UNSIGNED); (*it)->setup_conversion(thd, (uchar) (typecode & 0xff)); (*it)->sync_clones(); } @@ -929,7 +999,7 @@ static bool set_conversion_functions(Prepared_statement *stmt, static bool setup_conversion_functions(Prepared_statement *stmt, - uchar **data, uchar *data_end, + uchar **data, bool bulk_protocol= 0) { /* skip null bits */ @@ -942,7 +1012,7 @@ static bool setup_conversion_functions(Prepared_statement *stmt, if (*read_pos++) //types supplied / first execute { *data= read_pos; - bool res= set_conversion_functions(stmt, data, data_end); + bool res= set_conversion_functions(stmt, data); DBUG_RETURN(res); } *data= read_pos; @@ -1243,9 +1313,8 @@ static bool mysql_test_insert(Prepared_statement *stmt, table_list->table->insert_values=(uchar *)1; } - if (mysql_prepare_insert(thd, table_list, table_list->table, - fields, values, update_fields, update_values, - duplic, &unused_conds, FALSE)) + if (mysql_prepare_insert(thd, table_list, fields, values, update_fields, + update_values, duplic, &unused_conds, FALSE)) goto error; value_count= values->elements; @@ -2618,6 +2687,15 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length) if (stmt->prepare(packet, packet_length)) { + /* + Prepare failed and stmt will be freed. + Now we have to save the query_string in the so the + audit plugin later gets the meaningful notification. + */ + if (alloc_query(thd, stmt->query_string.str(), stmt->query_string.length())) + { + thd->set_query(0, 0); + } /* Statement map deletes statement on erase */ thd->stmt_map.erase(stmt); thd->clear_last_stmt(); @@ -2752,6 +2830,7 @@ bool Lex_prepared_stmt::get_dynamic_sql_string(THD *thd, void mysql_sql_stmt_prepare(THD *thd) { LEX *lex= thd->lex; + CSET_STRING orig_query= thd->query_string; const LEX_CSTRING *name= &lex->prepared_stmt.name(); Prepared_statement *stmt; LEX_CSTRING query; @@ -2822,7 +2901,16 @@ void mysql_sql_stmt_prepare(THD *thd) thd->m_statement_psi, stmt->name.str, stmt->name.length); - if (stmt->prepare(query.str, (uint) query.length)) + bool res= stmt->prepare(query.str, (uint) query.length); + /* + stmt->prepare() sets thd->query_string with the prepared + query, so the audit plugin gets adequate notification with the + mysqld_stmt_* set of functions. + But here we should restore the original query so it's mentioned in + logs properly. + */ + thd->set_query(orig_query); + if (res) { /* Statement map deletes the statement on erase */ thd->stmt_map.erase(stmt); @@ -2841,6 +2929,7 @@ void mysql_sql_stmt_prepare(THD *thd) void mysql_sql_stmt_execute_immediate(THD *thd) { LEX *lex= thd->lex; + CSET_STRING orig_query= thd->query_string; Prepared_statement *stmt; LEX_CSTRING query; DBUG_ENTER("mysql_sql_stmt_execute_immediate"); @@ -2889,6 +2978,14 @@ void mysql_sql_stmt_execute_immediate(THD *thd) thd->free_items(); thd->free_list= free_list_backup; + /* + stmt->execute_immediately() sets thd->query_string with the executed + query, so the audit plugin gets adequate notification with the + mysqld_stmt_* set of functions. + But here we should restore the original query so it's mentioned in + logs properly. + */ + thd->set_query_inner(orig_query); stmt->lex->restore_set_statement_var(); delete stmt; DBUG_VOID_RETURN; @@ -3110,11 +3207,19 @@ static void mysql_stmt_execute_common(THD *thd, void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length) { + const uint packet_min_lenght= 9; uchar *packet= (uchar*)packet_arg; // GCC 4.0.1 workaround + + DBUG_ENTER("mysqld_stmt_execute"); + + if (packet_length < packet_min_lenght) + { + my_error(ER_MALFORMED_PACKET, MYF(0)); + DBUG_VOID_RETURN; + } ulong stmt_id= uint4korr(packet); ulong flags= (ulong) packet[4]; uchar *packet_end= packet + packet_length; - DBUG_ENTER("mysqld_stmt_execute"); packet+= 9; /* stmt_id + 5 bytes of flags */ @@ -3170,6 +3275,84 @@ void mysqld_stmt_bulk_execute(THD *thd, char *packet_arg, uint packet_length) DBUG_VOID_RETURN; } +/** + Additional packet checks for direct execution + + @param thd THD handle + @param stmt prepared statement being directly executed + @param paket packet with parameters to bind + @param packet_end pointer to the byte after parameters end + @param bulk_op is it bulk operation + @param direct_exec is it direct execution + @param read_bytes need to read types (only with bulk_op) + + @retval true this parameter is wrong + @retval false this parameter is OK +*/ + +static bool +stmt_execute_packet_sanity_check(Prepared_statement *stmt, + uchar *packet, uchar *packet_end, + bool bulk_op, bool direct_exec, + bool read_types) +{ + + DBUG_ASSERT((!read_types) || (read_types && bulk_op)); + if (stmt->param_count > 0) + { + uint packet_length= static_cast<uint>(packet_end - packet); + uint null_bitmap_bytes= (bulk_op ? 0 : (stmt->param_count + 7)/8); + uint min_len_for_param_count = null_bitmap_bytes + + (bulk_op ? 0 : 1); /* sent types byte */ + + if (!bulk_op && packet_length >= min_len_for_param_count) + { + if ((read_types= packet[null_bitmap_bytes])) + { + /* + Should be 0 or 1. If the byte is not 1, that could mean, + e.g. that we read incorrect byte due to incorrect number + of sent parameters for direct execution (i.e. null bitmap + is shorter or longer, than it should be) + */ + if (packet[null_bitmap_bytes] != '\1') + { + return true; + } + } + } + + if (read_types) + { + /* 2 bytes per parameter of the type and flags */ + min_len_for_param_count+= 2*stmt->param_count; + } + else + { + /* + If types are not sent, there is nothing to do here. + But for direct execution types should always be sent + */ + return direct_exec; + } + + /* + If true, the packet is guaranteed too short for the number of + parameters in the PS + */ + return (packet_length < min_len_for_param_count); + } + else + { + /* + If there is no parameters, this should be normally already end + of the packet. If it's not - then error + */ + return (packet_end > packet); + } + return false; +} + /** Common part of prepared statement execution @@ -3205,10 +3388,33 @@ static void mysql_stmt_execute_common(THD *thd, if (!(stmt= find_prepared_statement(thd, stmt_id))) { char llbuf[22]; + /* + Did not find the statement with the provided stmt_id. + Set thd->query_string with the stmt_id so the + audit plugin gets the meaningful notification. + */ + if (alloc_query(thd, llbuf, strlen(llbuf))) + thd->set_query(0, 0); my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(sizeof(llbuf)), llstr(stmt_id, llbuf), "mysqld_stmt_execute"); DBUG_VOID_RETURN; } + + /* + In case of direct execution application decides how many parameters + to send. + + Thus extra checks are required to prevent crashes caused by incorrect + interpretation of the packet data. Plus there can be always a broken + evil client. + */ + if (stmt_execute_packet_sanity_check(stmt, packet, packet_end, bulk_op, + stmt_id == LAST_STMT_ID, read_types)) + { + my_error(ER_MALFORMED_PACKET, MYF(0)); + DBUG_VOID_RETURN; + } + stmt->read_types= read_types; #if defined(ENABLED_PROFILING) @@ -3969,6 +4175,19 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) DBUG_RETURN(TRUE); } + /* + We'd like to have thd->query to be set to the actual query + after the function ends. + This value will be sent to audit plugins later. + As the statement is created, the query will be stored + in statement's arena. Normally the statement lives longer than + the end of this query, so we can just set thd->query_string to + be the stmt->query_string. + Though errors can result in statement to be freed. These cases + should be handled appropriately. + */ + stmt_backup.query_string= thd->query_string; + old_stmt_arena= thd->stmt_arena; thd->stmt_arena= this; @@ -4131,7 +4350,7 @@ Prepared_statement::set_parameters(String *expanded_query, { #ifndef EMBEDDED_LIBRARY uchar *null_array= packet; - res= (setup_conversion_functions(this, &packet, packet_end) || + res= (setup_conversion_functions(this, &packet) || set_params(this, null_array, packet, packet_end, expanded_query)); #else /* @@ -4321,7 +4540,7 @@ Prepared_statement::execute_bulk_loop(String *expanded_query, return TRUE; } - if (!(sql_command_flags[lex->sql_command] & CF_SP_BULK_SAFE)) + if (!(sql_command_flags[lex->sql_command] & CF_PS_ARRAY_BINDING_SAFE)) { DBUG_PRINT("error", ("Command is not supported in bulk execution.")); my_error(ER_UNSUPPORTED_PS, MYF(0)); @@ -4331,7 +4550,7 @@ Prepared_statement::execute_bulk_loop(String *expanded_query, #ifndef EMBEDDED_LIBRARY if (read_types && - set_conversion_functions(this, &packet, packet_end)) + set_conversion_functions(this, &packet)) #else // bulk parameters are not supported for embedded, so it will an error #endif @@ -4353,7 +4572,7 @@ Prepared_statement::execute_bulk_loop(String *expanded_query, Here we set parameters for not optimized commands, optimized commands do it inside thier internal loop. */ - if (!(sql_command_flags[lex->sql_command] & CF_SP_BULK_OPTIMIZED)) + if (!(sql_command_flags[lex->sql_command] & CF_PS_ARRAY_BINDING_OPTIMIZED)) { if (set_bulk_parameters(TRUE)) { @@ -4504,6 +4723,15 @@ Prepared_statement::reprepare() */ thd->get_stmt_da()->clear_warning_info(thd->query_id); } + else + { + /* + Prepare failed and the 'copy' will be freed. + Now we have to restore the query_string in the so the + audit plugin later gets the meaningful notification. + */ + thd->set_query(query(), query_length()); + } return error; } @@ -5127,7 +5355,7 @@ public: Protocol_local(THD *thd_arg, ulong prealloc= 0) : Protocol_text(thd_arg, prealloc), - cur_data(0), first_data(0), data_tail(&first_data) + cur_data(0), first_data(0), data_tail(&first_data), alloc(0) {} protected: diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc index ae5a26e1529..8f87d633d19 100644 --- a/sql/sql_reload.cc +++ b/sql/sql_reload.cc @@ -218,6 +218,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options, thd->handler_tables_hash.records || thd->ull_hash.records || thd->global_read_lock.is_acquired() || + thd->mdl_backup_lock || thd->current_backup_stage != BACKUP_FINISHED ); @@ -530,7 +531,14 @@ bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables) if (thd->current_backup_stage != BACKUP_FINISHED) { my_error(ER_BACKUP_LOCK_IS_ACTIVE, MYF(0)); - return true; + goto error; + } + + /* Should not flush tables while BACKUP LOCK is active */ + if (thd->mdl_backup_lock) + { + my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); + goto error; } if (thd->lex->type & REFRESH_READ_LOCK) diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 0ac97bbeafd..77a1e46a75a 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -303,7 +303,8 @@ do_rename(THD *thd, TABLE_LIST *ren_table, const LEX_CSTRING *new_db, DBUG_RETURN(skip_error || if_exists ? 0 : 1); } - if (ha_check_if_updates_are_ignored(thd, hton, "RENAME")) + if (hton != view_pseudo_hton && + ha_check_if_updates_are_ignored(thd, hton, "RENAME")) { /* Shared table. Just drop the old .frm as it's not correct anymore diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 420a64ba827..74b9b4dc393 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -439,11 +439,14 @@ bool handle_select(THD *thd, LEX *lex, select_result *result, If LIMIT ROWS EXAMINED interrupted query execution, issue a warning, continue with normal processing and produce an incomplete query result. */ + bool saved_abort_on_warning= thd->abort_on_warning; + thd->abort_on_warning= false; push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT, ER_THD(thd, ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT), thd->accessed_rows_and_keys, thd->lex->limit_rows_examined->val_uint()); + thd->abort_on_warning= saved_abort_on_warning; thd->reset_killed(); } /* Disable LIMIT ROWS EXAMINED after query execution. */ @@ -743,7 +746,7 @@ bool vers_select_conds_t::init_from_sysvar(THD *thd) void vers_select_conds_t::print(String *str, enum_query_type query_type) const { - switch (type) { + switch (orig_type) { case SYSTEM_TIME_UNSPECIFIED: break; case SYSTEM_TIME_AS_OF: @@ -991,6 +994,7 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) case SQLCOM_SELECT: use_sysvar= true; /* fall through */ + case SQLCOM_CREATE_TABLE: case SQLCOM_INSERT_SELECT: case SQLCOM_REPLACE_SELECT: case SQLCOM_DELETE_MULTI: @@ -1608,7 +1612,7 @@ int JOIN::optimize() if (!(select_options & SELECT_DESCRIBE)) { /* Prepare to execute the query pushed into a foreign engine */ - res= select_lex->pushdown_select->init(); + res= select_lex->pushdown_select->prepare(); } with_two_phase_optimization= false; } @@ -2781,6 +2785,10 @@ int JOIN::optimize_stage2() (select_options & (SELECT_DESCRIBE | SELECT_NO_JOIN_CACHE)) | (select_lex->ftfunc_list->elements ? SELECT_NO_JOIN_CACHE : 0); + if (select_lex->options & OPTION_SCHEMA_TABLE && + optimize_schema_tables_reads(this)) + DBUG_RETURN(1); + if (make_join_readinfo(this, select_opts_for_readinfo, no_jbuf_after)) DBUG_RETURN(1); @@ -2957,10 +2965,6 @@ int JOIN::optimize_stage2() having_is_correlated= MY_TEST(having->used_tables() & OUTER_REF_TABLE_BIT); tmp_having= having; - if ((select_lex->options & OPTION_SCHEMA_TABLE) && - optimize_schema_tables_reads(this)) - DBUG_RETURN(TRUE); - if (unlikely(thd->is_error())) DBUG_RETURN(TRUE); @@ -4642,19 +4646,7 @@ mysql_select(THD *thd, TABLE_LIST *tables, List<Item> &fields, COND *conds, } /* Look for a table owned by an engine with the select_handler interface */ - select_lex->select_h= select_lex->find_select_handler(thd); - if (select_lex->select_h) - { - /* Create a Pushdown_select object for later execution of the query */ - if (!(select_lex->pushdown_select= - new (thd->mem_root) Pushdown_select(select_lex, - select_lex->select_h))) - { - delete select_lex->select_h; - select_lex->select_h= NULL; - DBUG_RETURN(TRUE); - } - } + select_lex->pushdown_select= select_lex->find_select_handler(thd); if ((err= join->optimize())) { @@ -18161,8 +18153,7 @@ public: bool add_schema_fields(THD *thd, TABLE *table, TMP_TABLE_PARAM *param, - const ST_SCHEMA_TABLE &schema_table, - const MY_BITMAP &bitmap); + const ST_SCHEMA_TABLE &schema_table); bool finalize(THD *thd, TABLE *table, TMP_TABLE_PARAM *param, bool do_not_open, bool keep_row_order); @@ -18271,8 +18262,7 @@ TABLE *Create_tmp_table::start(THD *thd, No need to change table name to lower case as we are only creating MyISAM, Aria or HEAP tables here */ - fn_format(path, path, mysql_tmpdir, "", - MY_REPLACE_EXT|MY_UNPACK_FILENAME); + fn_format(path, path, mysql_tmpdir, "", MY_REPLACE_EXT|MY_UNPACK_FILENAME); if (m_group) { @@ -19094,8 +19084,7 @@ err: bool Create_tmp_table::add_schema_fields(THD *thd, TABLE *table, TMP_TABLE_PARAM *param, - const ST_SCHEMA_TABLE &schema_table, - const MY_BITMAP &bitmap) + const ST_SCHEMA_TABLE &schema_table) { DBUG_ENTER("Create_tmp_table::add_schema_fields"); DBUG_ASSERT(table); @@ -19114,11 +19103,9 @@ bool Create_tmp_table::add_schema_fields(THD *thd, TABLE *table, for (fieldnr= 0; !defs[fieldnr].end_marker(); fieldnr++) { const ST_FIELD_INFO &def= defs[fieldnr]; - bool visible= bitmap_is_set(&bitmap, fieldnr); Record_addr addr(def.nullable()); const Type_handler *h= def.type_handler(); - Field *field= h->make_schema_field(&table->mem_root, table, - addr, def, visible); + Field *field= h->make_schema_field(&table->mem_root, table, addr, def); if (!field) { thd->mem_root= mem_root_save; @@ -19181,17 +19168,16 @@ TABLE *create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, TABLE *create_tmp_table_for_schema(THD *thd, TMP_TABLE_PARAM *param, const ST_SCHEMA_TABLE &schema_table, - const MY_BITMAP &bitmap, longlong select_options, const LEX_CSTRING &table_alias, - bool keep_row_order) + bool do_not_open, bool keep_row_order) { TABLE *table; Create_tmp_table maker(param, (ORDER *) NULL, false, false, select_options, HA_POS_ERROR); if (!(table= maker.start(thd, param, &table_alias)) || - maker.add_schema_fields(thd, table, param, schema_table, bitmap) || - maker.finalize(thd, table, param, false, keep_row_order)) + maker.add_schema_fields(thd, table, param, schema_table) || + maker.finalize(thd, table, param, do_not_open, keep_row_order)) { maker.cleanup_on_failure(thd, table); return NULL; @@ -19572,14 +19558,10 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, } } - if (unlikely((error= maria_create(share->path.str, - file_type, - share->keys, &keydef, - (uint) (*recinfo-start_recinfo), - start_recinfo, - share->uniques, &uniquedef, - &create_info, - create_flags)))) + if (unlikely((error= maria_create(share->path.str, file_type, share->keys, + &keydef, (uint) (*recinfo-start_recinfo), + start_recinfo, share->uniques, &uniquedef, + &create_info, create_flags)))) { table->file->print_error(error,MYF(0)); /* purecov: inspected */ table->db_stat=0; diff --git a/sql/sql_select.h b/sql/sql_select.h index 2087438e086..2aba63ddc79 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -2436,10 +2436,9 @@ TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, bool keep_row_order= FALSE); TABLE *create_tmp_table_for_schema(THD *thd, TMP_TABLE_PARAM *param, const ST_SCHEMA_TABLE &schema_table, - const MY_BITMAP &bitmap, longlong select_options, const LEX_CSTRING &alias, - bool keep_row_order); + bool do_not_open, bool keep_row_order); void free_tmp_table(THD *thd, TABLE *entry); bool create_internal_tmp_table_from_heap(THD *thd, TABLE *table, @@ -2517,29 +2516,6 @@ public: class select_handler; -class Pushdown_select: public Sql_alloc -{ -private: - bool is_analyze; - List<Item> result_columns; - bool send_result_set_metadata(); - bool send_data(); - bool send_eof(); - -public: - SELECT_LEX *select; - select_handler *handler; - - Pushdown_select(SELECT_LEX *sel, select_handler *h); - - ~Pushdown_select(); - - bool init(); - - int execute(); -}; - - bool test_if_order_compatible(SQL_I_List<ORDER> &a, SQL_I_List<ORDER> &b); int test_if_group_changed(List<Cached_item> &list); int create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 1e32e8b2925..c00476871e4 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3675,8 +3675,6 @@ static bool show_status_array(THD *thd, const char *wild, CHARSET_INFO *charset= system_charset_info; DBUG_ENTER("show_status_array"); - thd->count_cuted_fields= CHECK_FIELD_WARN; - prefix_end=strnmov(name_buffer, prefix, sizeof(name_buffer)-1); if (*prefix) *prefix_end++= '_'; @@ -3776,6 +3774,8 @@ static bool show_status_array(THD *thd, const char *wild, pos= get_one_variable(thd, var, scope, show_type, status_var, &charset, buff, &length); + if (table->field[1]->field_length) + thd->count_cuted_fields= CHECK_FIELD_WARN; table->field[1]->store(pos, (uint32) length, charset); thd->count_cuted_fields= CHECK_FIELD_IGNORE; table->field[1]->set_notnull(); @@ -5251,6 +5251,11 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) free_root(&tmp_mem_root, MY_MARK_BLOCKS_FREE); } } + if (thd->killed == ABORT_QUERY) + { + error= 0; + goto err; + } } } } @@ -8041,51 +8046,6 @@ ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx) return &schema_tables[schema_table_idx]; } -static void -mark_all_fields_used_in_query(THD *thd, - ST_FIELD_INFO *schema_fields, - MY_BITMAP *bitmap, - Item *all_items) -{ - Item *item; - DBUG_ENTER("mark_all_fields_used_in_query"); - - /* If not SELECT command, return all columns */ - if (thd->lex->sql_command != SQLCOM_SELECT && - thd->lex->sql_command != SQLCOM_SET_OPTION) - { - bitmap_set_all(bitmap); - DBUG_VOID_RETURN; - } - - for (item= all_items ; item ; item= item->next) - { - if (item->type() == Item::FIELD_ITEM) - { - ST_FIELD_INFO *fields= schema_fields; - uint count; - Item_field *item_field= (Item_field*) item; - - /* item_field can be '*' as this function is called before fix_fields */ - if (item_field->field_name.str == star_clex_str.str) - { - bitmap_set_all(bitmap); - break; - } - for (count=0; !fields->end_marker(); fields++, count++) - { - if (!my_strcasecmp(system_charset_info, fields->name().str, - item_field->field_name.str)) - { - bitmap_set_bit(bitmap, count); - break; - } - } - } - } - DBUG_VOID_RETURN; -} - /** Create information_schema table using schema_table data. @@ -8098,7 +8058,7 @@ mark_all_fields_used_in_query(THD *thd, 0<decimals<10 and 0<=length<100 . @param - thd thread handler + thd thread handler @param table_list Used to pass I_S table information(fields info, tables parameters etc) and table name. @@ -8109,32 +8069,16 @@ mark_all_fields_used_in_query(THD *thd, TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) { - uint field_count; - Item *all_items; + uint field_count= 0; TABLE *table; ST_SCHEMA_TABLE *schema_table= table_list->schema_table; - ST_FIELD_INFO *fields_info= schema_table->fields_info; - ST_FIELD_INFO *fields; - MY_BITMAP bitmap; - my_bitmap_map *buf; + ST_FIELD_INFO *fields= schema_table->fields_info; + bool need_all_fieds= table_list->schema_table_reformed || // SHOW command + thd->lex->only_view_structure(); // need table structure DBUG_ENTER("create_schema_table"); - for (field_count= 0, fields= fields_info; !fields->end_marker(); fields++) + for (; !fields->end_marker(); fields++) field_count++; - if (!(buf= (my_bitmap_map*) thd->alloc(bitmap_buffer_size(field_count)))) - DBUG_RETURN(NULL); - my_bitmap_init(&bitmap, buf, field_count, 0); - - if (!thd->stmt_arena->is_conventional() && - thd->mem_root != thd->stmt_arena->mem_root) - all_items= thd->stmt_arena->free_list; - else - all_items= thd->free_list; - - if (table_list->part_of_natural_join) - bitmap_set_all(&bitmap); - else - mark_all_fields_used_in_query(thd, fields_info, &bitmap, all_items); TMP_TABLE_PARAM *tmp_table_param = new (thd->mem_root) TMP_TABLE_PARAM; tmp_table_param->init(); @@ -8143,13 +8087,9 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) tmp_table_param->schema_table= 1; SELECT_LEX *select_lex= table_list->select_lex; bool keep_row_order= is_show_command(thd); - if (!(table= create_tmp_table_for_schema(thd, tmp_table_param, - *schema_table, bitmap, - (select_lex->options | - thd->variables.option_bits | - TMP_TABLE_ALL_COLUMNS), - table_list->alias, - keep_row_order))) + if (!(table= create_tmp_table_for_schema(thd, tmp_table_param, *schema_table, + (select_lex->options | thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS), + table_list->alias, !need_all_fieds, keep_row_order))) DBUG_RETURN(0); my_bitmap_map* bitmaps= (my_bitmap_map*) thd->alloc(bitmap_buffer_size(field_count)); @@ -8541,6 +8481,68 @@ end: } +static int optimize_schema_tables_memory_usage(TABLE_LIST *table_list) +{ + TABLE *table= table_list->table; + THD *thd=table->in_use; + if (!table->is_created()) + { + TMP_TABLE_PARAM *p= table_list->schema_table_param; + TMP_ENGINE_COLUMNDEF *from_recinfo, *to_recinfo; + DBUG_ASSERT(table->s->keys == 0); + DBUG_ASSERT(table->s->uniques == 0); + + // XXX HACK HACK HACK: in a stored function, RETURN (SELECT ...) + // enables warnings (in THD::sp_eval_expr) for the whole val_xxx/store pair, + // while the intention is to warn only for store(). Until this is + // fixed let's avoid data truncation warnings in I_S->fill_table() + if (thd->count_cuted_fields == CHECK_FIELD_IGNORE) + { + + uchar *cur= table->field[0]->ptr; + /* first recinfo could be a NULL bitmap, not an actual Field */ + from_recinfo= to_recinfo= p->start_recinfo + (cur != table->record[0]); + for (uint i=0; i < table->s->fields; i++, from_recinfo++) + { + Field *field= table->field[i]; + DBUG_ASSERT(field->vcol_info == 0); + DBUG_ASSERT(from_recinfo->length); + DBUG_ASSERT(from_recinfo->length == field->pack_length_in_rec()); + if (bitmap_is_set(table->read_set, i)) + { + field->move_field(cur); + *to_recinfo++= *from_recinfo; + cur+= from_recinfo->length; + } + else + { + field= new (thd->mem_root) Field_string(cur, 0, field->null_ptr, + field->null_bit, Field::NONE, + &field->field_name, field->dtcollation()); + field->init(table); + field->field_index= i; + DBUG_ASSERT(field->pack_length_in_rec() == 0); + table->field[i]= field; + } + } + if ((table->s->reclength= (ulong)(cur - table->record[0])) == 0) + { + /* all fields were optimized away. Force a non-0-length row */ + table->s->reclength= to_recinfo->length= 1; + to_recinfo++; + } + p->recinfo= to_recinfo; + } // XXX end of HACK HACK HACK + + // TODO switch from Aria to Memory if all blobs were optimized away? + if (instantiate_tmp_table(table, p->keyinfo, p->start_recinfo, &p->recinfo, + table_list->select_lex->options | thd->variables.option_bits)) + return 1; + } + return 0; +} + + /* This is the optimizer part of get_schema_tables_result(). */ @@ -8561,6 +8563,9 @@ bool optimize_schema_tables_reads(JOIN *join) TABLE_LIST *table_list= tab->table->pos_in_table_list; if (table_list->schema_table && thd->fill_information_schema_tables()) { + if (optimize_schema_tables_memory_usage(table_list)) + DBUG_RETURN(1); + /* A value of 0 indicates a dummy implementation */ if (table_list->schema_table->fill_table == 0) continue; @@ -8583,10 +8588,10 @@ bool optimize_schema_tables_reads(JOIN *join) cond= tab->cache_select->cond; } if (optimize_for_get_all_tables(thd, table_list, cond)) - DBUG_RETURN(TRUE); // Handle OOM + DBUG_RETURN(1); // Handle OOM } } - DBUG_RETURN(FALSE); + DBUG_RETURN(0); } @@ -8651,13 +8656,10 @@ bool get_schema_tables_result(JOIN *join, continue; /* - If schema table is already processed and - the statement is not a subselect then - we don't need to fill this table again. - If schema table is already processed and - schema_table_state != executed_place then - table is already processed and - we should skip second data processing. + If schema table is already processed and the statement is not a + subselect then we don't need to fill this table again. If schema table + is already processed and schema_table_state != executed_place then + table is already processed and we should skip second data processing. */ if (table_list->schema_table_state && (!is_subselect || table_list->schema_table_state != executed_place)) @@ -8719,8 +8721,7 @@ bool get_schema_tables_result(JOIN *join, It also means that an audit plugin cannot process the error correctly either. See also thd->clear_error() */ - thd->get_stmt_da()->push_warning(thd, - thd->get_stmt_da()->sql_errno(), + thd->get_stmt_da()->push_warning(thd, thd->get_stmt_da()->sql_errno(), thd->get_stmt_da()->get_sqlstate(), Sql_condition::WARN_LEVEL_ERROR, thd->get_stmt_da()->message()); diff --git a/sql/sql_sort.h b/sql/sql_sort.h index 3230052dd84..3b23328183c 100644 --- a/sql/sql_sort.h +++ b/sql/sql_sort.h @@ -255,10 +255,12 @@ class Sort_keys :public Sql_alloc, { public: Sort_keys(SORT_FIELD* arr, size_t count): - Sort_keys_array(arr, count), - m_using_packed_sortkeys(false), - size_of_packable_fields(0), - sort_length(0) + Sort_keys_array(arr, count), + m_using_packed_sortkeys(false), + size_of_packable_fields(0), + sort_length_with_original_values(0), + sort_length_with_memcmp_values(0), + parameters_computed(false) { DBUG_ASSERT(!is_null()); } @@ -280,14 +282,24 @@ public: return size_of_packable_fields; } - void set_sort_length(uint len) + void set_sort_length_with_original_values(uint len) { - sort_length= len; + sort_length_with_original_values= len; } - uint get_sort_length() + uint get_sort_length_with_original_values() { - return sort_length; + return sort_length_with_original_values; + } + + void set_sort_length_with_memcmp_values(uint len) + { + sort_length_with_memcmp_values= len; + } + + uint get_sort_length_with_memcmp_values() + { + return sort_length_with_memcmp_values; } static void store_sortkey_length(uchar *p, uint sz) @@ -307,9 +319,12 @@ public: void increment_original_sort_length(uint len) { - sort_length+= len; + sort_length_with_original_values+= len; } + bool is_parameters_computed() { return parameters_computed; } + void set_parameters_computed(bool val) { parameters_computed= val; } + static const uint size_of_length_field= 4; private: @@ -317,10 +332,21 @@ private: uint size_of_packable_fields; // Total length bytes for packable columns /* - The length that would be needed if we stored non-packed mem-comparable - images of fields? + The sort length for all the keyparts storing the original values + */ + uint sort_length_with_original_values; + + /* + The sort length for all the keyparts storing the mem-comparable images + */ + uint sort_length_with_memcmp_values; + + /* + TRUE parameters(like sort_length_* , size_of_packable_field) + are computed + FALSE otherwise. */ - uint sort_length; + bool parameters_computed; }; diff --git a/sql/sql_string.cc b/sql/sql_string.cc index e2defba434d..94f2e6fc8c6 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -767,10 +767,10 @@ void Static_binary_string::qs_append(double d) NULL); } -void Static_binary_string::qs_append(double *d) +void Static_binary_string::qs_append(const double *d) { double ld; - float8get(ld, (char*) d); + float8get(ld, (const char*) d); qs_append(ld); } @@ -858,7 +858,7 @@ int sortcmp(const String *s,const String *t, CHARSET_INFO *cs) int stringcmp(const String *s,const String *t) { uint32 s_len=s->length(),t_len=t->length(),len=MY_MIN(s_len,t_len); - int cmp= memcmp(s->ptr(), t->ptr(), len); + int cmp= len ? memcmp(s->ptr(), t->ptr(), len) : 0; return (cmp) ? cmp : (int) (s_len - t_len); } diff --git a/sql/sql_string.h b/sql/sql_string.h index 2ef817ea0ad..b3eca118b63 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -314,7 +314,8 @@ public: } void q_append(const char *data, size_t data_len) { - memcpy(Ptr + str_length, data, data_len); + if (data_len) + memcpy(Ptr + str_length, data, data_len); DBUG_ASSERT(str_length <= UINT_MAX32 - data_len); str_length += (uint)data_len; } @@ -345,7 +346,7 @@ public: void qs_append(const char *str, size_t len); void qs_append_hex(const char *str, uint32 len); void qs_append(double d); - void qs_append(double *d); + void qs_append(const double *d); inline void qs_append(const char c) { Ptr[str_length]= c; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 15d190c3139..c2ba9bcadfb 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -75,9 +75,8 @@ static int copy_data_between_tables(THD *, TABLE *,TABLE *, ha_rows *, ha_rows *, Alter_info::enum_enable_or_disable, Alter_table_ctx *); -static bool append_system_key_parts(THD *thd, HA_CREATE_INFO *create_info, - Alter_info *alter_info, KEY **key_info, - uint key_count); +static int append_system_key_parts(THD *thd, HA_CREATE_INFO *create_info, + Key *key); static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *, uint *, handler *, KEY **, uint *, int); static uint blob_length_by_type(enum_field_types type); @@ -1823,10 +1822,6 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) strxmov(shadow_frm_name, shadow_path, reg_ext, NullS); if (flags & WFRM_WRITE_SHADOW) { - if (append_system_key_parts(lpt->thd, lpt->create_info, lpt->alter_info, - &lpt->key_info_buffer, 0)) - DBUG_RETURN(true); - if (mysql_prepare_create_table(lpt->thd, lpt->create_info, lpt->alter_info, &lpt->db_options, lpt->table->file, &lpt->key_info_buffer, &lpt->key_count, @@ -2828,6 +2823,12 @@ bool log_drop_table(THD *thd, const LEX_CSTRING *db_name, append_identifier(thd, &query, table_name); query.append(STRING_WITH_LEN("/* Generated to handle " "failed CREATE OR REPLACE */")); + + /* + In case of temporary tables we don't have to log the database name + in the binary log. We log this for non temporary tables, as the slave + may use a filter to ignore queries for a specific database. + */ error= thd->binlog_query(THD::STMT_QUERY_TYPE, query.ptr(), query.length(), FALSE, FALSE, temporary_table, 0) > 0; @@ -3451,10 +3452,9 @@ Key * mysql_add_invisible_index(THD *thd, List<Key> *key_list, LEX_CSTRING* field_name, enum Key::Keytype type) { - Key *key= NULL; - key= new (thd->mem_root) Key(type, &null_clex_str, HA_KEY_ALG_UNDEF, - false, DDL_options(DDL_options::OPT_NONE)); - key->columns.push_back(new(thd->mem_root) Key_part_spec(field_name, 0), + Key *key= new (thd->mem_root) Key(type, &null_clex_str, HA_KEY_ALG_UNDEF, + false, DDL_options(DDL_options::OPT_NONE)); + key->columns.push_back(new(thd->mem_root) Key_part_spec(field_name, 0, true), thd->mem_root); key_list->push_back(key, thd->mem_root); return key; @@ -3552,7 +3552,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, Create_field *sql_field,*dup_field; uint field,null_fields,max_key_length; ulong record_offset= 0; - KEY *key_info; KEY_PART_INFO *key_part_info; int field_no,dup_no; int select_field_pos,auto_increment=0; @@ -3870,6 +3869,57 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, thd->abort_on_warning= sav_abort_on_warning; } } + + KEY *key_info= *key_info_buffer= (KEY*)thd->calloc(sizeof(KEY) * (*key_count)); + if (!*key_info_buffer) + DBUG_RETURN(true); // Out of memory + + key_iterator.rewind(); + while ((key=key_iterator++)) + { + if (key->name.str == ignore_key || key->type == Key::FOREIGN_KEY) + continue; + /* Create the key->ame based on the first column (if not given) */ + if (key->type == Key::PRIMARY) + { + if (primary_key) + { + my_message(ER_MULTIPLE_PRI_KEY, ER_THD(thd, ER_MULTIPLE_PRI_KEY), + MYF(0)); + DBUG_RETURN(true); + } + key_name=primary_key_name; + primary_key=1; + } + else if (!(key_name= key->name.str)) + { + auto field_name= key->columns.elem(0)->field_name; + it.rewind(); + while ((sql_field=it++) && + lex_string_cmp(system_charset_info, + &field_name, + &sql_field->field_name)); + if (sql_field) + field_name= sql_field->field_name; + key_name=make_unique_key_name(thd, field_name.str, + *key_info_buffer, key_info); + } + if (check_if_keyname_exists(key_name, *key_info_buffer, key_info)) + { + my_error(ER_DUP_KEYNAME, MYF(0), key_name); + DBUG_RETURN(true); + } + + key_info->name.str= (char*) key_name; + key_info->name.length= strlen(key_name); + key->name= key_info->name; + + int parts_added= append_system_key_parts(thd, create_info, key); + if (parts_added < 0) + DBUG_RETURN(true); + key_parts += parts_added; + key_info++; + } tmp=file->max_keys(); if (*key_count > tmp) { @@ -3877,11 +3927,11 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, DBUG_RETURN(TRUE); } - (*key_info_buffer)= key_info= (KEY*) thd->calloc(sizeof(KEY) * (*key_count)); key_part_info=(KEY_PART_INFO*) thd->calloc(sizeof(KEY_PART_INFO)*key_parts); - if (!*key_info_buffer || ! key_part_info) - DBUG_RETURN(TRUE); // Out of memory + if (!key_part_info) + DBUG_RETURN(true); // Out of memory + key_info= *key_info_buffer; key_iterator.rewind(); key_number=0; for (; (key=key_iterator++) ; key_number++) @@ -4028,7 +4078,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, /* Either field is not present or field visibility is > INVISIBLE_USER */ - if (!sql_field) + if (!sql_field || (sql_field->invisible > INVISIBLE_USER && + !column->generated)) { my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str); DBUG_RETURN(TRUE); @@ -4251,32 +4302,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, key_length+= key_part_length; key_part_info++; - - /* Create the key name based on the first column (if not given) */ - if (column_nr == 0) - { - if (key->type == Key::PRIMARY) - { - if (primary_key) - { - my_message(ER_MULTIPLE_PRI_KEY, ER_THD(thd, ER_MULTIPLE_PRI_KEY), - MYF(0)); - DBUG_RETURN(TRUE); - } - key_name=primary_key_name; - primary_key=1; - } - else if (!(key_name= key->name.str)) - key_name=make_unique_key_name(thd, sql_field->field_name.str, - *key_info_buffer, key_info); - if (check_if_keyname_exists(key_name, *key_info_buffer, key_info)) - { - my_error(ER_DUP_KEYNAME, MYF(0), key_name); - DBUG_RETURN(TRUE); - } - key_info->name.str= (char*) key_name; - key_info->name.length= strlen(key_name); - } } if (!key_info->name.str || check_column_name(key_info->name.str)) { @@ -4709,69 +4734,75 @@ bool Column_definition::sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root) } -static bool append_system_key_parts(THD *thd, HA_CREATE_INFO *create_info, - Alter_info *alter_info, KEY **key_info, - uint key_count) +/** + Appends key parts generated by mariadb server. + Adds row_end in UNIQUE keys for system versioning, + and period fields for WITHOUT OVERLAPS. + @param thd Thread data + @param create_info Table create info + @param key Parsed key + @return a number of key parts added to key. + */ +static int append_system_key_parts(THD *thd, HA_CREATE_INFO *create_info, + Key *key) { const Lex_ident &row_start_field= create_info->vers_info.as_row.start; const Lex_ident &row_end_field= create_info->vers_info.as_row.end; DBUG_ASSERT(!create_info->versioned() || (row_start_field && row_end_field)); - List_iterator<Key> key_it(alter_info->key_list); - Key *key= NULL; - - if (create_info->versioned()) + int result = 0; + if (create_info->versioned() && (key->type == Key::PRIMARY + || key->type == Key::UNIQUE)) { - while ((key=key_it++)) + Key_part_spec *key_part=NULL; + List_iterator<Key_part_spec> part_it(key->columns); + while ((key_part=part_it++)) { - if (key->type != Key::PRIMARY && key->type != Key::UNIQUE) - continue; - - Key_part_spec *key_part=NULL; - List_iterator<Key_part_spec> part_it(key->columns); - while ((key_part=part_it++)) - { - if (row_start_field.streq(key_part->field_name) || - row_end_field.streq(key_part->field_name)) - break; - } - if (!key_part) - key->columns.push_back(new Key_part_spec(&row_end_field, 0)); + if (row_start_field.streq(key_part->field_name) || + row_end_field.streq(key_part->field_name)) + break; } - key_it.rewind(); + if (!key_part) + { + key->columns.push_back(new (thd->mem_root) + Key_part_spec(&row_end_field, 0, true)); + result++; + } + } - while ((key=key_it++)) + if (key->without_overlaps) { - if (key->without_overlaps) + DBUG_ASSERT(key->type == Key::PRIMARY || key->type == Key::UNIQUE); + if (!create_info->period_info.is_set() + || !key->period.streq(create_info->period_info.name)) { - DBUG_ASSERT(key->type == Key::PRIMARY || key->type == Key::UNIQUE); - if (!create_info->period_info.is_set() - || !key->period.streq(create_info->period_info.name)) - { - my_error(ER_PERIOD_NOT_FOUND, MYF(0), key->period.str); - return true; - } + my_error(ER_PERIOD_NOT_FOUND, MYF(0), key->period.str); + return -1; + } - const auto &period_start= create_info->period_info.period.start; - const auto &period_end= create_info->period_info.period.end; - List_iterator<Key_part_spec> part_it(key->columns); - while (Key_part_spec *key_part= part_it++) + const auto &period_start= create_info->period_info.period.start; + const auto &period_end= create_info->period_info.period.end; + List_iterator<Key_part_spec> part_it(key->columns); + while (Key_part_spec *key_part= part_it++) + { + if (period_start.streq(key_part->field_name) + || period_end.streq(key_part->field_name)) { - if (period_start.streq(key_part->field_name) - || period_end.streq(key_part->field_name)) - { - my_error(ER_KEY_CONTAINS_PERIOD_FIELDS, MYF(0), key->name.str, - key_part->field_name); - return true; - } + my_error(ER_KEY_CONTAINS_PERIOD_FIELDS, MYF(0), key->name.str, + key_part->field_name.str); + return -1; } - key->columns.push_back(new Key_part_spec(&period_end, 0)); - key->columns.push_back(new Key_part_spec(&period_start, 0)); } + const auto &period= create_info->period_info.period; + key->columns.push_back(new (thd->mem_root) + Key_part_spec(&period.end, 0, true)); + key->columns.push_back(new (thd->mem_root) + Key_part_spec(&period.start, 0, true)); + result += 2; } - return false; + return result; } handler *mysql_create_frm_image(THD *thd, const LEX_CSTRING &db, @@ -5009,10 +5040,6 @@ handler *mysql_create_frm_image(THD *thd, const LEX_CSTRING &db, } #endif - if (append_system_key_parts(thd, create_info, alter_info, key_info, - *key_count)) - goto err; - if (mysql_prepare_create_table(thd, create_info, alter_info, &db_options, file, key_info, key_count, create_table_mode)) goto err; @@ -5079,6 +5106,7 @@ int create_table_impl(THD *thd, const LEX_CSTRING &orig_db, int error= 1; bool frm_only= create_table_mode == C_ALTER_TABLE_FRM_ONLY; bool internal_tmp_table= create_table_mode == C_ALTER_TABLE || frm_only; + handlerton *exists_hton; DBUG_ENTER("mysql_create_table_no_lock"); DBUG_PRINT("enter", ("db: '%s' table: '%s' tmp: %d path: %s", db.str, table_name.str, internal_tmp_table, path)); @@ -5167,8 +5195,16 @@ int create_table_impl(THD *thd, const LEX_CSTRING &orig_db, goto err; } - if (!internal_tmp_table && ha_table_exists(thd, &db, &table_name)) + if (!internal_tmp_table && ha_table_exists(thd, &db, &table_name, + &exists_hton)) { + if (ha_check_if_updates_are_ignored(thd, exists_hton, "CREATE")) + { + /* Don't create table. CREATE will still be logged in binary log */ + error= 0; + goto err; + } + if (options.or_replace()) { (void) delete_statistics_for_table(thd, &db, &table_name); @@ -5196,7 +5232,8 @@ int create_table_impl(THD *thd, const LEX_CSTRING &orig_db, thd->variables.option_bits|= OPTION_KEEP_LOG; thd->log_current_statement= 1; create_info->table_was_deleted= 1; - DBUG_EXECUTE_IF("send_kill_after_delete", thd->set_killed(KILL_QUERY); ); + DBUG_EXECUTE_IF("send_kill_after_delete", + thd->set_killed(KILL_QUERY); ); /* Restart statement transactions for the case of CREATE ... SELECT. @@ -5206,7 +5243,20 @@ int create_table_impl(THD *thd, const LEX_CSTRING &orig_db, goto err; } else if (options.if_not_exists()) + { + /* + We never come here as part of normal create table as table existance + is checked in open_and_lock_tables(). We may come here as part of + ALTER TABLE when converting a table for a distributed engine to a + a local one. + */ + + /* Log CREATE IF NOT EXISTS on slave for distributed engines */ + if (thd->slave_thread && (exists_hton && exists_hton->flags & + HTON_IGNORE_UPDATES)) + thd->log_current_statement= 1; goto warn; + } else { my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name.str); @@ -5437,12 +5487,21 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, thd->lex->create_info.options|= create_info->options; /* Open or obtain an exclusive metadata lock on table being created */ + create_table->db_type= 0; result= open_and_lock_tables(thd, *create_info, create_table, FALSE, 0); thd->lex->create_info.options= save_thd_create_info_options; if (result) { + if (thd->slave_thread && + !thd->is_error() && create_table->db_type && + (create_table->db_type->flags & HTON_IGNORE_UPDATES)) + { + /* Table existed in distributed engine. Log query to binary log */ + result= 0; + goto err; + } /* is_error() may be 0 if table existed and we generated a warning */ DBUG_RETURN(thd->is_error()); } @@ -5714,7 +5773,8 @@ mysql_rename_table(handlerton *base, const LEX_CSTRING *old_db, DBUG_ENTER("mysql_rename_table"); DBUG_ASSERT(base); DBUG_PRINT("enter", ("old: '%s'.'%s' new: '%s'.'%s'", - old_db->str, old_name->str, new_db->str, new_name->str)); + old_db->str, old_name->str, new_db->str, + new_name->str)); // Temporarily disable foreign key checks if (flags & NO_FK_CHECKS) @@ -5724,8 +5784,8 @@ mysql_rename_table(handlerton *base, const LEX_CSTRING *old_db, build_table_filename(from, sizeof(from) - 1, old_db->str, old_name->str, "", flags & FN_FROM_IS_TMP); - length= build_table_filename(to, sizeof(to) - 1, new_db->str, new_name->str, "", - flags & FN_TO_IS_TMP); + length= build_table_filename(to, sizeof(to) - 1, new_db->str, + new_name->str, "", flags & FN_TO_IS_TMP); // Check if we hit FN_REFLEN bytes along with file extension. if (length+reg_ext_length > FN_REFLEN) { @@ -6677,7 +6737,7 @@ remove_key: DBUG_ASSERT(key->or_replace()); Alter_drop::drop_type type= (key->type == Key::FOREIGN_KEY) ? Alter_drop::FOREIGN_KEY : Alter_drop::KEY; - Alter_drop *ad= new Alter_drop(type, key->name.str, FALSE); + Alter_drop *ad= new (thd->mem_root) Alter_drop(type, key->name.str, FALSE); if (ad != NULL) { // Adding the index into the drop list for replacing @@ -7171,9 +7231,8 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar, ha_alter_info->handler_flags|= ALTER_STORED_COLUMN_TYPE; } - /* Check if field was renamed */ - if (lex_string_cmp(system_charset_info, &field->field_name, - &new_field->field_name)) + /* Check if field was renamed (case-sensitive for detecting case change) */ + if (cmp(&field->field_name, &new_field->field_name)) { field->flags|= FIELD_IS_RENAMED; ha_alter_info->handler_flags|= ALTER_COLUMN_NAME; @@ -8823,8 +8882,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, key_part_length= 0; // Use whole field } key_part_length /= kfield->charset()->mbmaxlen; - key_parts.push_back(new (thd->mem_root) Key_part_spec( - &cfield->field_name, key_part_length), + key_parts.push_back(new (thd->mem_root) Key_part_spec(&cfield->field_name, + key_part_length, true), thd->mem_root); } if (table->s->tmp_table == NO_TMP_TABLE) @@ -9709,6 +9768,22 @@ static int create_table_for_inplace_alter(THD *thd, } +/* + log query if slave thread and send my_ok() + + Help function for mysql_alter_table() +*/ + +static bool log_and_ok(THD *thd) +{ + if (thd->slave_thread && + write_bin_log(thd, true, thd->query(), thd->query_length())) + return(true); + my_ok(thd); + return(0); +} + + /** Alter table @@ -9778,7 +9853,7 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, mysql_prepare_create_table(). */ bool varchar= create_info->varchar, table_creation_was_logged= 0; - bool binlog_done= 0, log_if_exists= 0; + bool binlog_as_create_select= 0, log_if_exists= 0; uint tables_opened; handlerton *new_db_type, *old_db_type; ha_rows copied=0, deleted=0; @@ -9843,10 +9918,7 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, table_list->mdl_request.type= MDL_EXCLUSIVE; /* This will only drop the .frm file and local tables, not shared ones */ error= mysql_rm_table(thd, table_list, 1, 0, 0, 1); - if (write_bin_log(thd, true, thd->query(), thd->query_length()) || error) - DBUG_RETURN(true); - my_ok(thd); - DBUG_RETURN(0); + DBUG_RETURN(log_and_ok(thd)); } /* @@ -9878,16 +9950,14 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, the statement as this slave may not have the table shared */ thd->clear_error(); - if (thd->slave_thread && - write_bin_log(thd, true, thd->query(), thd->query_length())) - DBUG_RETURN(true); - my_ok(thd); - DBUG_RETURN(0); + DBUG_RETURN(log_and_ok(thd)); } } DBUG_RETURN(true); } + table= table_list->table; + #ifdef WITH_WSREP if (WSREP(thd) && (thd->lex->sql_command == SQLCOM_ALTER_TABLE || @@ -9899,7 +9969,6 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, DEBUG_SYNC(thd, "alter_table_after_open_tables"); - table= table_list->table; if (table->versioned()) { if (handlerton *hton1= create_info->db_type) @@ -9938,12 +10007,17 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, Alter_table_ctx alter_ctx(thd, table_list, tables_opened, new_db, new_name); mdl_ticket= table->mdl_ticket; + /* + We have to do a check also after table is opened as there could be no + ENGINE= on the command line or the table could a partitioned S3 table. + */ if (table->file->check_if_updates_are_ignored("ALTER")) { /* Table is a shared table. Remove the .frm file. Discovery will create a new one if needed. */ + table->s->tdc->flushed= 1; // Force close of all instances if (thd->mdl_context.upgrade_shared_lock(mdl_ticket, MDL_EXCLUSIVE, thd->variables.lock_wait_timeout)) @@ -10700,7 +10774,6 @@ do_continue:; thd->variables.option_bits|= OPTION_BIN_COMMIT_OFF; res= (binlog_drop_table(thd, table) || binlog_create_table(thd, new_table, 1)); - thd->variables.option_bits&= ~OPTION_BIN_COMMIT_OFF; new_table->s->tmp_table= org_tmp_table; if (res) goto err_new_table_cleanup; @@ -10708,7 +10781,7 @@ do_continue:; ha_write_row() will log inserted rows in copy_data_between_tables(). No additional logging of query is needed */ - binlog_done= 1; + binlog_as_create_select= 1; DBUG_ASSERT(new_table->file->row_logging); new_table->mark_columns_needed_for_insert(); thd->binlog_write_table_map(new_table, 1); @@ -10764,10 +10837,21 @@ do_continue:; if (thd->rename_temporary_table(new_table, &alter_ctx.new_db, &alter_ctx.new_name)) goto err_new_table_cleanup; + + if (binlog_as_create_select) + { + /* + The original table is now deleted. Copy the + DROP + CREATE + data statement to the binary log + */ + thd->variables.option_bits&= ~OPTION_BIN_COMMIT_OFF; + (binlog_hton->commit)(binlog_hton, thd, 1); + } + /* We don't replicate alter table statement on temporary tables */ if (!thd->is_current_stmt_binlog_format_row() && table_creation_was_logged && - !binlog_done && + !binlog_as_create_select && write_bin_log_with_if_exists(thd, true, false, log_if_exists)) DBUG_RETURN(true); my_free(const_cast<uchar*>(frm.str)); @@ -10927,6 +11011,15 @@ do_continue:; NO_FRM_RENAME | (engine_changed ? 0 : FN_IS_TMP)); } + if (binlog_as_create_select) + { + /* + The original table is now deleted. Copy the + DROP + CREATE + data statement to the binary log + */ + thd->variables.option_bits&= ~OPTION_BIN_COMMIT_OFF; + binlog_hton->commit(binlog_hton, thd, 1); + } if (error) { @@ -10939,6 +11032,7 @@ do_continue:; } end_inplace: + thd->variables.option_bits&= ~OPTION_BIN_COMMIT_OFF; if (thd->locked_tables_list.reopen_tables(thd, false)) goto err_with_mdl_after_alter; @@ -10950,7 +11044,7 @@ end_inplace: DBUG_ASSERT(!(mysql_bin_log.is_open() && thd->is_current_stmt_binlog_format_row() && (create_info->tmp_table()))); - if (!binlog_done) + if (!binlog_as_create_select) { if (write_bin_log_with_if_exists(thd, true, false, log_if_exists)) DBUG_RETURN(true); @@ -10968,6 +11062,8 @@ end_inplace: } end_temporary: + thd->variables.option_bits&= ~OPTION_BIN_COMMIT_OFF; + my_snprintf(alter_ctx.tmp_buff, sizeof(alter_ctx.tmp_buff), ER_THD(thd, ER_INSERT_INFO), (ulong) (copied + deleted), (ulong) deleted, @@ -10978,6 +11074,8 @@ end_temporary: err_new_table_cleanup: DBUG_PRINT("error", ("err_new_table_cleanup")); + thd->variables.option_bits&= ~OPTION_BIN_COMMIT_OFF; + my_free(const_cast<uchar*>(frm.str)); /* No default value was provided for a DATE/DATETIME field, the @@ -11015,7 +11113,7 @@ err_with_mdl_after_alter: We can't reset error as we will return 'true' below and the server expects that error is set */ - if (!binlog_done) + if (!binlog_as_create_select) write_bin_log_with_if_exists(thd, FALSE, FALSE, log_if_exists); err_with_mdl: @@ -11110,6 +11208,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, bool make_versioned= !from->versioned() && to->versioned(); bool make_unversioned= from->versioned() && !to->versioned(); bool keep_versioned= from->versioned() && to->versioned(); + bool bulk_insert_started= 0; Field *to_row_start= NULL, *to_row_end= NULL, *from_row_end= NULL; MYSQL_TIME query_start; DBUG_ENTER("copy_data_between_tables"); @@ -11148,6 +11247,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, to->file->extra(HA_EXTRA_PREPARE_FOR_ALTER_TABLE); to->file->ha_start_bulk_insert(from->file->stats.records, ignore ? 0 : HA_CREATE_UNIQUE_INDEX_BY_SORT); + bulk_insert_started= 1; mysql_stage_set_work_estimated(thd->m_stage_progress_psi, from->file->stats.records); List_iterator<Create_field> it(create); @@ -11416,6 +11516,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, to->file->print_error(my_errno,MYF(0)); error= 1; } + bulk_insert_started= 0; if (!ignore) to->file->extra(HA_EXTRA_END_ALTER_COPY); @@ -11429,7 +11530,10 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, error= 1; err: - /* Free resources */ + if (bulk_insert_started) + (void) to->file->ha_end_bulk_insert(); + +/* Free resources */ if (init_read_record_done) end_read_record(&info); delete [] copy; diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 677e6cfa510..9417ec667ff 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -1826,7 +1826,7 @@ bool Table_triggers_list::drop_all_triggers(THD *thd, const LEX_CSTRING *db, TABLE table; char path[FN_REFLEN]; bool result= 0; - DBUG_ENTER("Triggers::drop_all_triggers"); + DBUG_ENTER("Table_triggers_list::drop_all_triggers"); table.reset(); init_sql_alloc(key_memory_Table_trigger_dispatcher, diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 22375d2962c..00b9c71cc37 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -2655,8 +2655,7 @@ Field *Type_handler_set::make_conversion_table_field(MEM_ROOT *root, Field *Type_handler_enum::make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const + const ST_FIELD_INFO &def) const { LEX_CSTRING name= def.name(); const Typelib *typelib= def.typelib(); @@ -3861,8 +3860,7 @@ Field *Type_handler_set::make_table_field(MEM_ROOT *root, Field *Type_handler_float::make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const + const ST_FIELD_INFO &def) const { LEX_CSTRING name= def.name(); return new (root) @@ -3876,8 +3874,7 @@ Field *Type_handler_float::make_schema_field(MEM_ROOT *root, TABLE *table, Field *Type_handler_double::make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const + const ST_FIELD_INFO &def) const { LEX_CSTRING name= def.name(); return new (root) @@ -3892,8 +3889,7 @@ Field *Type_handler_double::make_schema_field(MEM_ROOT *root, TABLE *table, Field *Type_handler_decimal_result::make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const + const ST_FIELD_INFO &def) const { LEX_CSTRING name= def.name(); uint dec= def.decimal_scale(); @@ -3909,28 +3905,20 @@ Field *Type_handler_decimal_result::make_schema_field(MEM_ROOT *root, Field *Type_handler_blob_common::make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const + const ST_FIELD_INFO &def) const { LEX_CSTRING name= def.name(); - if (show_field) - { - return new (root) - Field_blob(addr.ptr(), addr.null_ptr(), addr.null_bit(), - Field::NONE, &name, table->s, - length_bytes(), - &my_charset_bin); - } - else - return new (root) - Field_null(addr.ptr(), 0, Field::NONE, &name, &my_charset_bin); + return new (root) + Field_blob(addr.ptr(), addr.null_ptr(), addr.null_bit(), + Field::NONE, &name, table->s, + length_bytes(), + &my_charset_bin); } Field *Type_handler_varchar::make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const + const ST_FIELD_INFO &def) const { DBUG_ASSERT(def.char_length()); LEX_CSTRING name= def.name(); @@ -3944,7 +3932,7 @@ Field *Type_handler_varchar::make_schema_field(MEM_ROOT *root, TABLE *table, field->field_length= octet_length; return field; } - else if (show_field) + else { return new (root) Field_varstring(addr.ptr(), octet_length, @@ -3953,16 +3941,12 @@ Field *Type_handler_varchar::make_schema_field(MEM_ROOT *root, TABLE *table, Field::NONE, &name, table->s, system_charset_info); } - else - return new (root) - Field_null(addr.ptr(), 0, Field::NONE, &name, system_charset_info); } Field *Type_handler_tiny::make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const + const ST_FIELD_INFO &def) const { LEX_CSTRING name= def.name(); return new (root) @@ -3974,8 +3958,7 @@ Field *Type_handler_tiny::make_schema_field(MEM_ROOT *root, TABLE *table, Field *Type_handler_short::make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const + const ST_FIELD_INFO &def) const { LEX_CSTRING name= def.name(); return new (root) @@ -3987,8 +3970,7 @@ Field *Type_handler_short::make_schema_field(MEM_ROOT *root, TABLE *table, Field *Type_handler_long::make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const + const ST_FIELD_INFO &def) const { LEX_CSTRING name= def.name(); return new (root) @@ -4000,8 +3982,7 @@ Field *Type_handler_long::make_schema_field(MEM_ROOT *root, TABLE *table, Field *Type_handler_longlong::make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const + const ST_FIELD_INFO &def) const { LEX_CSTRING name= def.name(); return new (root) @@ -4013,8 +3994,7 @@ Field *Type_handler_longlong::make_schema_field(MEM_ROOT *root, TABLE *table, Field *Type_handler_date_common::make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const + const ST_FIELD_INFO &def) const { LEX_CSTRING name= def.name(); return new (root) @@ -4025,8 +4005,7 @@ Field *Type_handler_date_common::make_schema_field(MEM_ROOT *root, TABLE *table, Field *Type_handler_time_common::make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const + const ST_FIELD_INFO &def) const { LEX_CSTRING name= def.name(); return new_Field_time(root, @@ -4038,8 +4017,7 @@ Field *Type_handler_time_common::make_schema_field(MEM_ROOT *root, TABLE *table, Field *Type_handler_datetime_common::make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const + const ST_FIELD_INFO &def) const { LEX_CSTRING name= def.name(); return new (root) Field_datetimef(addr.ptr(), @@ -5703,10 +5681,12 @@ cmp_item *Type_handler_timestamp_common::make_cmp_item(THD *thd, /***************************************************************************/ -static int srtcmp_in(CHARSET_INFO *cs, const String *x,const String *y) +static int srtcmp_in(const void *cs_, const void *x_, const void *y_) { - return cs->strnncollsp(x->ptr(), x->length(), - y->ptr(), y->length()); + const CHARSET_INFO *cs= static_cast<const CHARSET_INFO *>(cs_); + const String *x= static_cast<const String *>(x_); + const String *y= static_cast<const String *>(y_); + return cs->strnncollsp(x->ptr(), x->length(), y->ptr(), y->length()); } in_vector *Type_handler_string_result::make_in_vector(THD *thd, @@ -6956,6 +6936,24 @@ bool Type_handler_string_result:: /***************************************************************************/ +const Vers_type_handler* Type_handler_temporal_result::vers() const +{ + return &vers_type_timestamp; +} + +const Vers_type_handler* Type_handler_string_result::vers() const +{ + return &vers_type_timestamp; +} + +const Vers_type_handler* Type_handler_blob_common::vers() const + +{ + return &vers_type_timestamp; +} + +/***************************************************************************/ + uint Type_handler::Item_time_precision(THD *thd, Item *item) const { return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); diff --git a/sql/sql_type.h b/sql/sql_type.h index 41e840d9ed7..db4f67e343f 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -374,7 +374,7 @@ class Dec_ptr_and_buffer: public Dec_ptr protected: my_decimal m_buffer; public: - int round_to(my_decimal *to, uint scale, decimal_round_mode mode) + int round_to(my_decimal *to, int scale, decimal_round_mode mode) { DBUG_ASSERT(m_ptr); return m_ptr->round_to(to, scale, mode); @@ -383,6 +383,14 @@ public: { return round_to(&m_buffer, scale, mode); } + int round_self_if_needed(int scale, decimal_round_mode mode) + { + if (scale >= m_ptr->frac) + return E_DEC_OK; + int res= m_ptr->round_to(&m_buffer, scale, mode); + m_ptr= &m_buffer; + return res; + } String *to_string_round(String *to, uint dec) { /* @@ -3934,8 +3942,7 @@ public: virtual Field *make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const + const ST_FIELD_INFO &def) const { DBUG_ASSERT(0); return NULL; @@ -3980,7 +3987,6 @@ public: SORT_FIELD_ATTR *attr) const= 0; virtual bool is_packable() const { return false; } - virtual uint32 max_display_length(const Item *item) const= 0; virtual uint32 Item_decimal_notation_int_digits(const Item *item) const { return 0; } virtual uint32 calc_pack_length(uint32 length) const= 0; @@ -4817,8 +4823,7 @@ public: Field *make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const override; + const ST_FIELD_INFO &def) const override; Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const override; void make_sort_key_part(uchar *to, Item *item, @@ -5249,7 +5254,7 @@ public: bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const override; bool Item_func_div_fix_length_and_dec(Item_func_div *) const override; bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const override; - const Vers_type_handler *vers() const override { return &vers_type_timestamp; } + const Vers_type_handler *vers() const override; }; @@ -5286,7 +5291,7 @@ public: void sort_length(THD *thd, const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const override; - bool is_packable()const override { return true; } + bool is_packable() const override { return true; } bool union_element_finalize(const Item * item) const override; uint calc_key_length(const Column_definition &def) const override; bool Column_definition_prepare_stage1(THD *thd, @@ -5416,7 +5421,7 @@ public: bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const override; bool Item_func_div_fix_length_and_dec(Item_func_div *) const override; bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const override; - const Vers_type_handler *vers() const override { return &vers_type_timestamp; } + const Vers_type_handler *vers() const override; }; @@ -5477,8 +5482,7 @@ public: Field *make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const override; + const ST_FIELD_INFO &def) const override; Field *make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, const LEX_CSTRING *name, @@ -5529,8 +5533,7 @@ public: Field *make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const override; + const ST_FIELD_INFO &def) const override; Field *make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, const LEX_CSTRING *name, @@ -5581,8 +5584,7 @@ public: Field *make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const override; + const ST_FIELD_INFO &def) const override; Field *make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, const LEX_CSTRING *name, @@ -5648,8 +5650,7 @@ public: Field *make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const override; + const ST_FIELD_INFO &def) const override; Field *make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, const LEX_CSTRING *name, @@ -5879,8 +5880,7 @@ public: Field *make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const override; + const ST_FIELD_INFO &def) const override; Field *make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, const LEX_CSTRING *name, @@ -5935,8 +5935,7 @@ public: Field *make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const override; + const ST_FIELD_INFO &def) const override; Field *make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, const LEX_CSTRING *name, @@ -5991,8 +5990,7 @@ public: Field *make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const override; + const ST_FIELD_INFO &def) const override; Item_literal *create_literal_item(THD *thd, const char *str, size_t length, CHARSET_INFO *cs, bool send_error) const override; @@ -6202,8 +6200,7 @@ public: Field *make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const override; + const ST_FIELD_INFO &def) const override; Item_literal *create_literal_item(THD *thd, const char *str, size_t length, CHARSET_INFO *cs, bool send_error) const override; @@ -6333,8 +6330,7 @@ public: Field *make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const override; + const ST_FIELD_INFO &def) const override; Item *create_typecast_item(THD *thd, Item *item, const Type_cast_attributes &attr) const override; bool validate_implicit_default_value(THD *thd, const Column_definition &def) @@ -6909,8 +6905,7 @@ public: Field *make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const override; + const ST_FIELD_INFO &def) const override; Field *make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, const LEX_CSTRING *name, @@ -7030,8 +7025,7 @@ public: Field *make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const override; + const ST_FIELD_INFO &def) const override; Field *make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, const LEX_CSTRING *name, @@ -7039,7 +7033,7 @@ public: const Bit_addr &bit, const Column_definition_attributes *attr, uint32 flags) const override; - const Vers_type_handler *vers() const override { return &vers_type_timestamp; } + const Vers_type_handler *vers() const override; }; @@ -7214,8 +7208,7 @@ public: Field *make_schema_field(MEM_ROOT *root, TABLE *table, const Record_addr &addr, - const ST_FIELD_INFO &def, - bool show_field) const override; + const ST_FIELD_INFO &def) const override; }; diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 5df9e7dd47d..07dd3b1f6ca 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -58,6 +58,8 @@ static udf_func *add_udf(LEX_CSTRING *name, Item_result ret, const char *dl, Item_udftype typ); static void del_udf(udf_func *udf); static void *find_udf_dl(const char *dl); +static bool find_udf_everywhere(THD* thd, const LEX_CSTRING &name, + TABLE *table); static const char *init_syms(udf_func *tmp, char *nm) { @@ -430,6 +432,41 @@ static udf_func *add_udf(LEX_CSTRING *name, Item_result ret, const char *dl, return tmp; } +/** + Find record with the udf in the udf func table + + @param exact_name udf name + @param table table of mysql.func + + @retval TRUE found + @retral FALSE not found +*/ + +static bool find_udf_in_table(const LEX_CSTRING &exact_name, TABLE *table) +{ + table->use_all_columns(); + table->field[0]->store(exact_name.str, exact_name.length, &my_charset_bin); + return (!table->file->ha_index_read_idx_map(table->record[0], 0, + (uchar*) table->field[0]->ptr, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)); +} + +static bool remove_udf_in_table(const LEX_CSTRING &exact_name, TABLE *table) +{ + if (find_udf_in_table(exact_name, table)) + { + int error; + if ((error= table->file->ha_delete_row(table->record[0]))) + { + table->file->print_error(error, MYF(0)); + return TRUE; + } + } + return FALSE; +} + + /* Drop user defined function. @@ -446,8 +483,7 @@ static int mysql_drop_function_internal(THD *thd, udf_func *udf, TABLE *table) { DBUG_ENTER("mysql_drop_function_internal"); - const char *exact_name_str= udf->name.str; - size_t exact_name_len= udf->name.length; + const LEX_CSTRING exact_name= udf->name; del_udf(udf); /* @@ -460,18 +496,17 @@ static int mysql_drop_function_internal(THD *thd, udf_func *udf, TABLE *table) if (!table) DBUG_RETURN(1); - table->use_all_columns(); - table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin); - if (!table->file->ha_index_read_idx_map(table->record[0], 0, - (uchar*) table->field[0]->ptr, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) - { - int error; - if (unlikely((error= table->file->ha_delete_row(table->record[0])))) - table->file->print_error(error, MYF(0)); - } - DBUG_RETURN(0); + bool ret= remove_udf_in_table(exact_name, table); + DBUG_RETURN(ret); +} + + +static TABLE *open_udf_func_table(THD *thd) +{ + TABLE_LIST tables; + tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_FUNC_NAME, + &MYSQL_FUNC_NAME, TL_WRITE); + return open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT); } @@ -518,8 +553,7 @@ int mysql_create_function(THD *thd,udf_func *udf) if (check_ident_length(&udf->name)) DBUG_RETURN(1); - tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_FUNC_NAME, 0, TL_WRITE); - table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT); + table= open_udf_func_table(thd); mysql_rwlock_wrlock(&THR_LOCK_udf); DEBUG_SYNC(current_thd, "mysql_create_function_after_lock"); @@ -620,42 +654,65 @@ err: } -int mysql_drop_function(THD *thd, const LEX_CSTRING *udf_name) +enum drop_udf_result mysql_drop_function(THD *thd, const LEX_CSTRING *udf_name) { TABLE *table; - TABLE_LIST tables; udf_func *udf; DBUG_ENTER("mysql_drop_function"); + if (thd->locked_tables_mode) + { + my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); + DBUG_RETURN(UDF_DEL_RESULT_ERROR); + } + + if (!(table= open_udf_func_table(thd))) + DBUG_RETURN(UDF_DEL_RESULT_ERROR); + + // Fast pre-check + if (!mysql_rwlock_tryrdlock(&THR_LOCK_udf)) + { + bool found= find_udf_everywhere(thd, *udf_name, table); + mysql_rwlock_unlock(&THR_LOCK_udf); + if (!found) + { + close_mysql_tables(thd); + DBUG_RETURN(UDF_DEL_RESULT_ABSENT); + } + } + if (!initialized) { + close_mysql_tables(thd); if (opt_noacl) - my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str); - else - my_message(ER_OUT_OF_RESOURCES, ER_THD(thd, ER_OUT_OF_RESOURCES), - MYF(0)); - DBUG_RETURN(1); - } + DBUG_RETURN(UDF_DEL_RESULT_ABSENT); // SP should be checked - tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_FUNC_NAME, 0, TL_WRITE); - table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT); + my_message(ER_OUT_OF_RESOURCES, ER_THD(thd, ER_OUT_OF_RESOURCES), MYF(0)); + DBUG_RETURN(UDF_DEL_RESULT_ERROR); + } mysql_rwlock_wrlock(&THR_LOCK_udf); + + // re-check under protection + if (!find_udf_everywhere(thd, *udf_name, table)) + { + close_mysql_tables(thd); + mysql_rwlock_unlock(&THR_LOCK_udf); + DBUG_RETURN(UDF_DEL_RESULT_ABSENT); + } + + if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 0)) + goto err; + + DEBUG_SYNC(current_thd, "mysql_drop_function_after_lock"); + if (!(udf= (udf_func*) my_hash_search(&udf_hash, (uchar*) udf_name->str, (uint) udf_name->length)) ) { - if (thd->lex->check_exists) - { - push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_FUNCTION_NOT_DEFINED, - ER_THD(thd, ER_FUNCTION_NOT_DEFINED), - udf_name->str); - goto done; - } - - my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str); - goto err; + if (remove_udf_in_table(*udf_name, table)) + goto err; + goto done; } if (mysql_drop_function_internal(thd, udf, table)) @@ -669,13 +726,24 @@ done: while binlogging, to avoid binlog inconsistency. */ if (write_bin_log(thd, TRUE, thd->query(), thd->query_length())) - DBUG_RETURN(1); + DBUG_RETURN(UDF_DEL_RESULT_ERROR); - DBUG_RETURN(0); + close_mysql_tables(thd); + DBUG_RETURN(UDF_DEL_RESULT_DELETED); err: + close_mysql_tables(thd); mysql_rwlock_unlock(&THR_LOCK_udf); - DBUG_RETURN(1); + DBUG_RETURN(UDF_DEL_RESULT_ERROR); +} + +static bool find_udf_everywhere(THD* thd, const LEX_CSTRING &name, + TABLE *table) +{ + if (initialized && my_hash_search(&udf_hash, (uchar*) name.str, name.length)) + return true; + + return find_udf_in_table(name, table); } #endif /* HAVE_DLOPEN */ diff --git a/sql/sql_udf.h b/sql/sql_udf.h index ac58a176fed..cb1954353fa 100644 --- a/sql/sql_udf.h +++ b/sql/sql_udf.h @@ -155,7 +155,13 @@ void udf_init(void),udf_free(void); udf_func *find_udf(const char *name, size_t size, bool mark_used=0); void free_udf(udf_func *udf); int mysql_create_function(THD *thd,udf_func *udf); -int mysql_drop_function(THD *thd, const LEX_CSTRING *name); +enum drop_udf_result +{ + UDF_DEL_RESULT_ABSENT, + UDF_DEL_RESULT_DELETED, + UDF_DEL_RESULT_ERROR +}; +enum drop_udf_result mysql_drop_function(THD *thd, const LEX_CSTRING *name); #else static inline void udf_init(void) { } static inline void udf_free(void) { } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index fccc2a426c4..9e6a1cd3bb4 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -189,6 +189,13 @@ static bool check_fields(THD *thd, TABLE_LIST *table, List<Item> &items, my_error(ER_IT_IS_A_VIEW, MYF(0), table->table_name.str); return TRUE; } + if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), + "updating and querying the same temporal periods table"); + + return true; + } DBUG_ASSERT(thd->lex->sql_command == SQLCOM_UPDATE); for (List_iterator_fast<Item> it(items); (item=it++);) { @@ -419,6 +426,14 @@ int mysql_update(THD *thd, DBUG_PRINT("info", ("Switch to multi-update")); /* pass counter value */ thd->lex->table_count= table_count; + if (thd->lex->period_conditions.is_set()) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), + "updating and querying the same temporal periods table"); + + DBUG_RETURN(1); + } + /* convert to multiupdate */ DBUG_RETURN(2); } @@ -542,6 +557,8 @@ int mysql_update(THD *thd, query_plan.set_no_partitions(); if (thd->lex->describe || thd->lex->analyze_stmt) goto produce_explain_and_leave; + if (thd->is_error()) + DBUG_RETURN(1); my_ok(thd); // No matching records DBUG_RETURN(0); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e54b335e418..1b37896f18b 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2015, Oracle and/or its affiliates. - Copyright (c) 2010, 2020, MariaDB Corporation. + Copyright (c) 2010, 2020, 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 @@ -115,6 +115,14 @@ int yylex(void *yylval, void *yythd); #endif +static Item* escape(THD *thd) +{ + thd->lex->escape_used= false; + const char *esc= thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES ? "" : "\\"; + return new (thd->mem_root) Item_string_ascii(thd, esc, MY_TEST(esc[0])); +} + + /** @brief Bison callback to report a syntax/OOM error @@ -325,16 +333,16 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - We should not introduce new conflicts any more. + We should not introduce any further shift/reduce conflicts. */ /* Start SQL_MODE_DEFAULT_SPECIFIC */ -%expect 37 +%expect 53 /* End SQL_MODE_DEFAULT_SPECIFIC */ /* Start SQL_MODE_ORACLE_SPECIFIC -%expect 40 +%expect 56 End SQL_MODE_ORACLE_SPECIFIC */ @@ -1142,16 +1150,6 @@ End SQL_MODE_ORACLE_SPECIFIC */ %token <kwd> XML_SYM %token <kwd> YEAR_SYM /* SQL-2003-R */ - -/* - Give ESCAPE (in LIKE) a very low precedence. - This allows the concatenation operator || to be used on the right - side of "LIKE" with sql_mode=PIPES_AS_CONCAT (without ORACLE): - SELECT 'ab' LIKE 'a'||'b'||'c'; -*/ -%left PREC_BELOW_ESCAPE -%left ESCAPE_SYM - /* A dummy token to force the priority of table_ref production in a join. */ %left CONDITIONLESS_JOIN %left JOIN_SYM INNER_SYM STRAIGHT_JOIN CROSS LEFT RIGHT ON_SYM USING @@ -1162,10 +1160,12 @@ End SQL_MODE_ORACLE_SPECIFIC */ %left AND_SYM AND_AND_SYM %left PREC_BELOW_NOT -%left NOT_SYM -%left BETWEEN_SYM CASE_SYM WHEN_SYM THEN_SYM ELSE -%left '=' EQUAL_SYM GE '>' LE '<' NE IS LIKE SOUNDS_SYM REGEXP IN_SYM +%nonassoc NOT_SYM +%left '=' EQUAL_SYM GE '>' LE '<' NE +%nonassoc IS +%right BETWEEN_SYM +%left LIKE SOUNDS_SYM REGEXP IN_SYM %left '|' %left '&' %left SHIFT_LEFT SHIFT_RIGHT @@ -1173,9 +1173,9 @@ End SQL_MODE_ORACLE_SPECIFIC */ %left '*' '/' '%' DIV_SYM MOD_SYM %left '^' %left MYSQL_CONCAT_SYM -%left NEG '~' NOT2_SYM BINARY -%left COLLATE_SYM -%left SUBQUERY_AS_EXPR +%nonassoc NEG '~' NOT2_SYM BINARY +%nonassoc COLLATE_SYM +%nonassoc SUBQUERY_AS_EXPR /* Tokens that can change their meaning from identifier to something else @@ -1448,7 +1448,6 @@ End SQL_MODE_ORACLE_SPECIFIC */ select_sublist_qualified_asterisk expr_or_default set_expr_or_default signed_literal expr_or_literal - opt_escape sp_opt_default simple_ident_nospvar field_or_var limit_option @@ -7757,6 +7756,8 @@ alter_list_item: } | ALTER opt_column opt_if_exists_table_element field_ident SET DEFAULT column_default_expr { + if (check_expression($7, &$4, VCOL_DEFAULT)) + MYSQL_YYABORT; if (unlikely(Lex->add_alter_list($4, $7, $3))) MYSQL_YYABORT; } @@ -9299,59 +9300,59 @@ expr: if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bool_pri IS TRUE_SYM %prec IS + | expr IS TRUE_SYM %prec IS { $$= new (thd->mem_root) Item_func_istrue(thd, $1); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bool_pri IS not TRUE_SYM %prec IS + | expr IS not TRUE_SYM %prec IS { $$= new (thd->mem_root) Item_func_isnottrue(thd, $1); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bool_pri IS FALSE_SYM %prec IS + | expr IS FALSE_SYM %prec IS { $$= new (thd->mem_root) Item_func_isfalse(thd, $1); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bool_pri IS not FALSE_SYM %prec IS + | expr IS not FALSE_SYM %prec IS { $$= new (thd->mem_root) Item_func_isnotfalse(thd, $1); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bool_pri IS UNKNOWN_SYM %prec IS + | expr IS UNKNOWN_SYM %prec IS { $$= new (thd->mem_root) Item_func_isnull(thd, $1); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bool_pri IS not UNKNOWN_SYM %prec IS + | expr IS not UNKNOWN_SYM %prec IS { $$= new (thd->mem_root) Item_func_isnotnull(thd, $1); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bool_pri %prec PREC_BELOW_NOT - ; - -bool_pri: - bool_pri IS NULL_SYM %prec IS + | expr IS NULL_SYM %prec PREC_BELOW_NOT { $$= new (thd->mem_root) Item_func_isnull(thd, $1); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bool_pri IS not NULL_SYM %prec IS + | expr IS not NULL_SYM %prec IS { $$= new (thd->mem_root) Item_func_isnotnull(thd, $1); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bool_pri EQUAL_SYM predicate %prec EQUAL_SYM + | bool_pri + ; + +bool_pri: + bool_pri EQUAL_SYM predicate %prec EQUAL_SYM { $$= new (thd->mem_root) Item_func_equal(thd, $1, $3); if (unlikely($$ == NULL)) @@ -9373,42 +9374,42 @@ bool_pri: ; predicate: - bit_expr IN_SYM subquery + predicate IN_SYM subquery { $$= new (thd->mem_root) Item_in_subselect(thd, $1, $3); - if (unlikely($$ == NULL)) + if (unlikely(!$$)) MYSQL_YYABORT; } - | bit_expr not IN_SYM subquery + | predicate not IN_SYM subquery { Item *item= new (thd->mem_root) Item_in_subselect(thd, $1, $4); - if (unlikely(item == NULL)) + if (unlikely(!item)) MYSQL_YYABORT; $$= negate_expression(thd, item); - if (unlikely($$ == NULL)) + if (unlikely(!$$)) MYSQL_YYABORT; } - | bit_expr IN_SYM '(' expr ')' + | predicate IN_SYM '(' expr ')' { $$= handle_sql2003_note184_exception(thd, $1, true, $4); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bit_expr IN_SYM '(' expr ',' expr_list ')' - { + | predicate IN_SYM '(' expr ',' expr_list ')' + { $6->push_front($4, thd->mem_root); $6->push_front($1, thd->mem_root); $$= new (thd->mem_root) Item_func_in(thd, *$6); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bit_expr not IN_SYM '(' expr ')' + | predicate not IN_SYM '(' expr ')' { $$= handle_sql2003_note184_exception(thd, $1, false, $5); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bit_expr not IN_SYM '(' expr ',' expr_list ')' + | predicate not IN_SYM '(' expr ',' expr_list ')' { $7->push_front($5, thd->mem_root); $7->push_front($1, thd->mem_root); @@ -9417,13 +9418,13 @@ predicate: MYSQL_YYABORT; $$= item->neg_transformer(thd); } - | bit_expr BETWEEN_SYM bit_expr AND_SYM predicate + | predicate BETWEEN_SYM predicate AND_SYM predicate %prec BETWEEN_SYM { $$= new (thd->mem_root) Item_func_between(thd, $1, $3, $5); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate + | predicate not BETWEEN_SYM predicate AND_SYM predicate %prec BETWEEN_SYM { Item_func_between *item; item= new (thd->mem_root) Item_func_between(thd, $1, $4, $6); @@ -9431,7 +9432,7 @@ predicate: MYSQL_YYABORT; $$= item->neg_transformer(thd); } - | bit_expr SOUNDS_SYM LIKE bit_expr + | predicate SOUNDS_SYM LIKE predicate { Item *item1= new (thd->mem_root) Item_func_soundex(thd, $1); Item *item4= new (thd->mem_root) Item_func_soundex(thd, $4); @@ -9441,28 +9442,41 @@ predicate: if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bit_expr LIKE bit_expr opt_escape + | predicate LIKE predicate { - $$= new (thd->mem_root) Item_func_like(thd, $1, $3, $4, - Lex->escape_used); - if (unlikely($$ == NULL)) + $$= new (thd->mem_root) Item_func_like(thd, $1, $3, escape(thd), false); + if (unlikely(!$$)) MYSQL_YYABORT; } - | bit_expr not LIKE bit_expr opt_escape + | predicate LIKE predicate ESCAPE_SYM predicate %prec LIKE { - Item *item= new (thd->mem_root) Item_func_like(thd, $1, $4, $5, - Lex->escape_used); - if (unlikely(item == NULL)) + Lex->escape_used= true; + $$= new (thd->mem_root) Item_func_like(thd, $1, $3, $5, true); + if (unlikely(!$$)) + MYSQL_YYABORT; + } + | predicate not LIKE predicate + { + Item *item= new (thd->mem_root) Item_func_like(thd, $1, $4, escape(thd), false); + if (unlikely(!item)) MYSQL_YYABORT; $$= item->neg_transformer(thd); } - | bit_expr REGEXP bit_expr + | predicate not LIKE predicate ESCAPE_SYM predicate %prec LIKE + { + Lex->escape_used= true; + Item *item= new (thd->mem_root) Item_func_like(thd, $1, $4, $6, true); + if (unlikely(!item)) + MYSQL_YYABORT; + $$= item->neg_transformer(thd); + } + | predicate REGEXP predicate { $$= new (thd->mem_root) Item_func_regex(thd, $1, $3); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bit_expr not REGEXP bit_expr + | predicate not REGEXP predicate { Item *item= new (thd->mem_root) Item_func_regex(thd, $1, $4); if (unlikely(item == NULL)) @@ -10592,18 +10606,19 @@ function_call_generic: names are resolved with the following order: - MySQL native functions, - User Defined Functions, + - Constructors, like POINT(1,1) - Stored Functions (assuming the current <use> database) This will be revised with WL#2128 (SQL PATH) */ - if ((h= Type_handler::handler_by_name(thd, $1)) && - (item= h->make_constructor_item(thd, $4))) + if ((builder= find_native_function_builder(thd, &$1))) { - // Found a constructor with a proper argument count + item= builder->create_func(thd, &$1, $4); } - else if ((builder= find_native_function_builder(thd, &$1))) + else if ((h= Type_handler::handler_by_name(thd, $1)) && + (item= h->make_constructor_item(thd, $4))) { - item= builder->create_func(thd, &$1, $4); + // Found a constructor with a proper argument count } else { @@ -11904,23 +11919,6 @@ opt_having_clause: } ; -opt_escape: - ESCAPE_SYM simple_expr - { - Lex->escape_used= TRUE; - $$= $2; - } - | /* empty */ %prec PREC_BELOW_ESCAPE - { - Lex->escape_used= FALSE; - $$= ((thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) ? - new (thd->mem_root) Item_string_ascii(thd, "", 0) : - new (thd->mem_root) Item_string_ascii(thd, "\\", 1)); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - } - ; - /* group by statement in select */ @@ -14181,6 +14179,8 @@ backup_statements: } | LOCK_SYM { + if (unlikely(Lex->sphead)) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "BACKUP LOCK")); if (Lex->main_select_push()) MYSQL_YYABORT; } @@ -14194,6 +14194,8 @@ backup_statements: } | UNLOCK_SYM { + if (unlikely(Lex->sphead)) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "BACKUP UNLOCK")); /* Table list is empty for unlock */ Lex->sql_command= SQLCOM_BACKUP_LOCK; } diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic index c21a4a83165..2bd6ee6467d 100644 --- a/sql/sys_vars.ic +++ b/sql/sys_vars.ic @@ -769,8 +769,12 @@ public: void session_save_default(THD *, set_var *) override { DBUG_ASSERT(FALSE); } - void global_save_default(THD *, set_var *) override - { DBUG_ASSERT(FALSE); } + void global_save_default(THD *thd, set_var *var) override + { + char *ptr= (char*)(intptr)option.def_value; + var->save_result.string_value.str= ptr; + var->save_result.string_value.length= ptr ? strlen(ptr) : 0; + } bool session_update(THD *, set_var *) override { diff --git a/sql/table.cc b/sql/table.cc index 779ce768eee..c48a6fed89a 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2367,7 +2367,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, comment_pos+= comment_length; } - if ((uchar) strpos[13] == (uchar) MYSQL_TYPE_VIRTUAL) + if ((uchar) strpos[13] == (uchar) MYSQL_TYPE_VIRTUAL + && likely(share->mysql_version >= 100000)) { /* MariaDB version 10.0 version. @@ -2417,7 +2418,18 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, interval_nr= (uint) strpos[12]; enum_field_types field_type= (enum_field_types) strpos[13]; if (!(handler= Type_handler::get_handler_by_real_type(field_type))) - goto err; // Not supported field type + { + if (field_type == 245 && + share->mysql_version >= 50700) // a.k.a MySQL 5.7 JSON + { + share->incompatible_version|= HA_CREATE_USED_ENGINE; + const LEX_CSTRING mysql_json{STRING_WITH_LEN("MYSQL_JSON")}; + handler= Type_handler::handler_by_name_or_error(thd, mysql_json); + } + + if (!handler) + goto err; // Not supported field type + } handler= handler->type_handler_frm_unpack(strpos); if (handler->Column_definition_attributes_frm_unpack(&attr, share, strpos, @@ -8498,28 +8510,39 @@ public: @details The function computes the values of the virtual columns of the table and stores them in the table record buffer. + This will be done even if is_error() is set either when function was called + or by calculating the virtual function, as most calls to this + function doesn't check the result. We also want to ensure that as many + fields as possible has the right value so that we can optionally + return the partly-faulty-row from a storage engine with a virtual + field that gives an error on storage for an existing row. + + @todo + Ensure that all caller checks the value of this function and + either properly ignores it (and resets the error) or sends the + error forward to the caller. @retval 0 Success @retval - >0 Error occurred when storing a virtual field value + >0 Error occurred when storing a virtual field value or potentially + is_error() was set when function was called. */ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode) { DBUG_ENTER("TABLE::update_virtual_fields"); - DBUG_PRINT("enter", ("update_mode: %d", update_mode)); + DBUG_PRINT("enter", ("update_mode: %d is_error: %d", update_mode, + in_use->is_error())); Field **vfield_ptr, *vf; Query_arena backup_arena; Turn_errors_to_warnings_handler Suppress_errors; - int error; bool handler_pushed= 0, update_all_columns= 1; DBUG_ASSERT(vfield); if (h->keyread_enabled()) DBUG_RETURN(0); - error= 0; in_use->set_n_backup_active_arena(expr_arena, &backup_arena); /* When reading or deleting row, ignore errors from virtual columns */ @@ -8542,7 +8565,7 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode) } /* Iterate over virtual fields in the table */ - for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++) + for (vfield_ptr= vfield; *vfield_ptr ; vfield_ptr++) { vf= (*vfield_ptr); Virtual_column_info *vcol_info= vf->vcol_info; @@ -8593,8 +8616,7 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode) int field_error __attribute__((unused)) = 0; /* Compute the actual value of the virtual fields */ DBUG_FIX_WRITE_SET(vf); - if (vcol_info->expr->save_in_field(vf, 0)) - field_error= error= 1; + field_error= vcol_info->expr->save_in_field(vf, 0); DBUG_RESTORE_WRITE_SET(vf); DBUG_PRINT("info", ("field '%s' - updated error: %d", vf->field_name.str, field_error)); @@ -8719,6 +8741,7 @@ int TABLE::period_make_insert(Item *src, Field *dst) { THD *thd= in_use; + file->store_auto_increment(); store_record(this, record[1]); int res= src->save_in_field(dst, true); @@ -8737,6 +8760,8 @@ int TABLE::period_make_insert(Item *src, Field *dst) TRG_ACTION_AFTER, true); restore_record(this, record[1]); + if (res) + file->restore_auto_increment(); return res; } diff --git a/sql/table.h b/sql/table.h index 63bcc69b6bc..71884c919ad 100644 --- a/sql/table.h +++ b/sql/table.h @@ -2244,7 +2244,6 @@ struct TABLE_LIST parsing 'this' is a NATURAL/USING join iff (natural_join != NULL). */ TABLE_LIST *natural_join; - bool part_of_natural_join; /* True if 'this' represents a nested join that is a NATURAL JOIN. For one of the operands of 'this', the member 'natural_join' points diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc index b26f41c6e9a..168579d984b 100644 --- a/sql/threadpool_common.cc +++ b/sql/threadpool_common.cc @@ -317,6 +317,16 @@ static void handle_wait_timeout(THD *thd) thd->net.error= 2; } +/** Check if some client data is cached in thd->net or thd->net.vio */ +static bool has_unread_data(THD* thd) +{ + NET *net= &thd->net; + if (net->compress && net->remain_in_buf) + return true; + Vio *vio= net->vio; + return vio->has_data(vio); +} + /** Process a single client request or a single batch. @@ -351,7 +361,6 @@ static int threadpool_process_request(THD *thd) */ for(;;) { - Vio *vio; thd->net.reading_or_writing= 0; if (mysql_audit_release_required(thd)) mysql_audit_release(thd); @@ -367,8 +376,7 @@ static int threadpool_process_request(THD *thd) set_thd_idle(thd); - vio= thd->net.vio; - if (!vio->has_data(vio)) + if (!has_unread_data(thd)) { /* More info on this debug sync is in sql_parse.cc*/ DEBUG_SYNC(thd, "before_do_command_net_read"); diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index 51c23085717..4461d73a928 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -158,7 +158,8 @@ extern const char* wsrep_provider_vendor; extern char* wsrep_provider_capabilities; extern char* wsrep_cluster_capabilities; -int wsrep_show_status(THD *thd, SHOW_VAR *var, char *buff); +int wsrep_show_status(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *status_var, enum_var_type scope); int wsrep_show_ready(THD *thd, SHOW_VAR *var, char *buff); void wsrep_free_status(THD *thd); void wsrep_update_cluster_state_uuid(const char* str); diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc index f8c3f4d299b..25992459060 100644 --- a/sql/wsrep_sst.cc +++ b/sql/wsrep_sst.cc @@ -1825,6 +1825,35 @@ static int sst_donate_other (const char* method, return arg.err; } +/* return true if character can be a part of a filename */ +static bool filename_char(int const c) +{ + return isalnum(c) || (c == '-') || (c == '_') || (c == '.'); +} + +/* return true if character can be a part of an address string */ +static bool address_char(int const c) +{ + return filename_char(c) || + (c == ':') || (c == '[') || (c == ']') || (c == '/'); +} + +static bool check_request_str(const char* const str, + bool (*check) (int c)) +{ + for (size_t i(0); str[i] != '\0'; ++i) + { + if (!check(str[i])) + { + WSREP_WARN("Illegal character in state transfer request: %i (%c).", + str[i], str[i]); + return true; + } + } + + return false; +} + int wsrep_sst_donate(const std::string& msg, const wsrep::gtid& current_gtid, const bool bypass) @@ -1836,8 +1865,21 @@ int wsrep_sst_donate(const std::string& msg, const char* method= msg.data(); size_t method_len= strlen (method); + + if (check_request_str(method, filename_char)) + { + WSREP_ERROR("Bad SST method name. SST canceled."); + return WSREP_CB_FAILURE; + } + const char* data= method + method_len + 1; + if (check_request_str(data, address_char)) + { + WSREP_ERROR("Bad SST address string. SST canceled."); + return WSREP_CB_FAILURE; + } + wsp::env env(NULL); if (env.error()) { diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc index 8c9170a3745..97aca456369 100644 --- a/sql/wsrep_var.cc +++ b/sql/wsrep_var.cc @@ -921,7 +921,8 @@ static void export_wsrep_status_to_mysql(THD* thd) mysql_status_vars[wsrep_status_len].type = SHOW_LONG; } -int wsrep_show_status (THD *thd, SHOW_VAR *var, char *buff) +int wsrep_show_status (THD *thd, SHOW_VAR *var, void *, + system_status_var *, enum_var_type) { /* Note that we should allow show status like 'wsrep%' even when WSREP(thd) is false. */ diff --git a/sql/xa.cc b/sql/xa.cc index e8e6dc43c56..15833377fb6 100644 --- a/sql/xa.cc +++ b/sql/xa.cc @@ -689,7 +689,7 @@ bool trans_xa_commit(THD *thd) { DEBUG_SYNC(thd, "trans_xa_commit_after_acquire_commit_lock"); - res= MY_TEST(ha_commit_one_phase(thd, 1, 1)); + res= MY_TEST(ha_commit_one_phase(thd, 1)); if (res) my_error(ER_XAER_RMERR, MYF(0)); else |