diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:04:16 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:04:16 +0000 |
commit | a68fb2d8219f6bccc573009600e9f23e89226a5e (patch) | |
tree | d742d35d14ae816e99293d2b01face30e9f3a46b /storage/perfschema/table_uvar_by_thread.cc | |
parent | Initial commit. (diff) | |
download | mariadb-10.6-upstream.tar.xz mariadb-10.6-upstream.zip |
Adding upstream version 1:10.6.11.upstream/1%10.6.11upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'storage/perfschema/table_uvar_by_thread.cc')
-rw-r--r-- | storage/perfschema/table_uvar_by_thread.cc | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/storage/perfschema/table_uvar_by_thread.cc b/storage/perfschema/table_uvar_by_thread.cc new file mode 100644 index 00000000..43b9bf34 --- /dev/null +++ b/storage/perfschema/table_uvar_by_thread.cc @@ -0,0 +1,336 @@ +/* Copyright (c) 2013, 2022, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License, version 2.0, for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_uvar_by_thread.cc + Table USER_VARIABLES_BY_THREAD (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_uvar_by_thread.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" + +/* Iteration on THD from the sql layer. */ +#include "sql_class.h" +#include "mysqld_thd_manager.h" + +class Find_thd_user_var : public Find_THD_Impl +{ +public: + Find_thd_user_var(THD *unsafe_thd) + : m_unsafe_thd(unsafe_thd) + {} + + virtual bool operator()(THD *thd) + { + if (thd != m_unsafe_thd) + return false; + + if (thd->user_vars.records == 0) + return false; + + mysql_mutex_lock(&thd->LOCK_thd_data); + return true; + } + +private: + THD *m_unsafe_thd; +}; + +void User_variables::materialize(PFS_thread *pfs, THD *thd) +{ + reset(); + + m_pfs= pfs; + m_thread_internal_id= pfs->m_thread_internal_id; + m_array.reserve(thd->user_vars.records); + + user_var_entry *sql_uvar; + + uint index= 0; + User_variable empty; + + /* Protects thd->user_vars. */ + mysql_mutex_assert_owner(&thd->LOCK_thd_data); + + for (;;) + { + sql_uvar= reinterpret_cast<user_var_entry*> (my_hash_element(& thd->user_vars, index)); + if (sql_uvar == NULL) + break; + + /* + m_array is a container of objects (not pointers) + + Naive code can: + - build locally a new entry + - add it to the container + but this causes useless object construction, destruction, and deep copies. + + What we do here: + - add a dummy (empty) entry + - the container does a deep copy on something empty, + so that there is nothing to copy. + - get a reference to the entry added in the container + - complete -- in place -- the entry initialization + */ + m_array.push(empty); + User_variable & pfs_uvar= *m_array.back(); + + /* Copy VARIABLE_NAME */ + const char *name= sql_uvar->name.str; + size_t name_length= sql_uvar->name.length; + DBUG_ASSERT(name_length <= sizeof(pfs_uvar.m_name)); + pfs_uvar.m_name.make_row(name, name_length); + + /* Copy VARIABLE_VALUE */ + bool null_value; + String *str_value; + String str_buffer; + uint decimals= 0; + str_value= sql_uvar->val_str(& null_value, & str_buffer, decimals); + if (str_value != NULL) + { + pfs_uvar.m_value.make_row(str_value->ptr(), str_value->length()); + } + else + { + pfs_uvar.m_value.make_row(NULL, 0); + } + + index++; + } +} + +THR_LOCK table_uvar_by_thread::m_table_lock; + +PFS_engine_table_share_state +table_uvar_by_thread::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_uvar_by_thread::m_share= +{ + { C_STRING_WITH_LEN("user_variables_by_thread") }, + &pfs_readonly_acl, + table_uvar_by_thread::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_uvar_by_thread::get_row_count, + sizeof(pos_t), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE user_variables_by_thread(" + "THREAD_ID BIGINT unsigned not null comment 'The thread identifier of the session in which the variable is defined.'," + "VARIABLE_NAME VARCHAR(64) not null comment 'The variable name, without the leading @ character.'," + "VARIABLE_VALUE LONGBLOB comment 'The variable value')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_uvar_by_thread::create(void) +{ + return new table_uvar_by_thread(); +} + +ha_rows +table_uvar_by_thread::get_row_count(void) +{ + /* + This is an estimate only, not a hard limit. + The row count is given as a multiple of thread_max, + so that a join between: + - table performance_schema.threads + - table performance_schema.user_variables_by_thread + will still evaluate relative table sizes correctly + when deciding a join order. + */ + return global_thread_container.get_row_count() * 10; +} + +table_uvar_by_thread::table_uvar_by_thread() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_uvar_by_thread::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_uvar_by_thread::rnd_next(void) +{ + PFS_thread *thread; + bool has_more_thread= true; + + for (m_pos.set_at(&m_next_pos); + has_more_thread; + m_pos.next_thread()) + { + thread= global_thread_container.get(m_pos.m_index_1, & has_more_thread); + if (thread != NULL) + { + if (materialize(thread) == 0) + { + const User_variable *uvar= m_THD_cache.get(m_pos.m_index_2); + if (uvar != NULL) + { + make_row(thread, uvar); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_uvar_by_thread::rnd_pos(const void *pos) +{ + PFS_thread *thread; + + set_position(pos); + + thread= global_thread_container.get(m_pos.m_index_1); + if (thread != NULL) + { + if (materialize(thread) == 0) + { + const User_variable *uvar= m_THD_cache.get(m_pos.m_index_2); + if (uvar != NULL) + { + make_row(thread, uvar); + return 0; + } + } + } + + return HA_ERR_RECORD_DELETED; +} + +int table_uvar_by_thread::materialize(PFS_thread *thread) +{ + if (m_THD_cache.is_materialized(thread)) + return 0; + + if (! thread->m_lock.is_populated()) + return 1; + + THD *unsafe_thd= thread->m_thd; + if (unsafe_thd == NULL) + return 1; + + Find_thd_user_var finder(unsafe_thd); + THD *safe_thd= Global_THD_manager::get_instance()->find_thd(&finder); + if (safe_thd == NULL) + return 1; + + m_THD_cache.materialize(thread, safe_thd); + mysql_mutex_unlock(&safe_thd->LOCK_thd_data); + return 0; +} + +void table_uvar_by_thread +::make_row(PFS_thread *thread, const User_variable *uvar) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + /* Protect this reader against a thread termination */ + thread->m_lock.begin_optimistic_lock(&lock); + + m_row.m_thread_internal_id= thread->m_thread_internal_id; + + /* uvar is materialized, pointing to it directly. */ + m_row.m_variable_name= & uvar->m_name; + m_row.m_variable_value= & uvar->m_value; + + if (! thread->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; +} + +int table_uvar_by_thread +::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + assert(m_row.m_variable_name != NULL); + assert(m_row.m_variable_value != NULL); + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* THREAD_ID */ + set_field_ulonglong(f, m_row.m_thread_internal_id); + break; + case 1: /* VARIABLE_NAME */ + set_field_varchar_utf8(f, + m_row.m_variable_name->m_str, + m_row.m_variable_name->m_length); + break; + case 2: /* VARIABLE_VALUE */ + if (m_row.m_variable_value->get_value_length() > 0) + { + set_field_blob(f, + m_row.m_variable_value->get_value(), + static_cast<uint>(m_row.m_variable_value->get_value_length())); + } + else + { + f->set_null(); + } + break; + default: + assert(false); + } + } + } + + return 0; +} + |