1 /* Copyright (c) 2015, 2021, Oracle and/or its affiliates.
2 
3   This program is free software; you can redistribute it and/or modify
4   it under the terms of the GNU General Public License, version 2.0,
5   as published by the Free Software Foundation.
6 
7   This program is also distributed with certain software (including
8   but not limited to OpenSSL) that is licensed under separate terms,
9   as designated in a particular file or component or in included license
10   documentation.  The authors of MySQL hereby grant you an additional
11   permission to link the program and your derivative works with the
12   separately licensed software that they have included with MySQL.
13 
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License, version 2.0, for more details.
18 
19   You should have received a copy of the GNU General Public License
20   along with this program; if not, write to the Free Software
21   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 /**
24   @file storage/perfschema/table_variables_by_thread.cc
25   Table VARIABLES_BY_THREAD (implementation).
26 */
27 
28 #include "my_global.h"
29 #include "table_variables_by_thread.h"
30 #include "my_thread.h"
31 #include "pfs_instr_class.h"
32 #include "pfs_column_types.h"
33 #include "pfs_column_values.h"
34 #include "pfs_global.h"
35 
36 THR_LOCK table_variables_by_thread::m_table_lock;
37 
38 static const TABLE_FIELD_TYPE field_types[]=
39 {
40   {
41     { C_STRING_WITH_LEN("THREAD_ID") },
42     { C_STRING_WITH_LEN("bigint(20)") },
43     { NULL, 0}
44   },
45   {
46     { C_STRING_WITH_LEN("VARIABLE_NAME") },
47     { C_STRING_WITH_LEN("varchar(64)") },
48     { NULL, 0}
49   },
50   {
51     { C_STRING_WITH_LEN("VARIABLE_VALUE") },
52     { C_STRING_WITH_LEN("varchar(1024)") },
53     { NULL, 0}
54   }
55 };
56 
57 TABLE_FIELD_DEF
58 table_variables_by_thread::m_field_def=
59 { 3, field_types };
60 
61 PFS_engine_table_share
62 table_variables_by_thread::m_share=
63 {
64   { C_STRING_WITH_LEN("variables_by_thread") },
65   &pfs_readonly_acl,
66   table_variables_by_thread::create,
67   NULL, /* write_row */
68   NULL, /* delete_all_rows */
69   table_variables_by_thread::get_row_count,
70   sizeof(pos_t),
71   &m_table_lock,
72   &m_field_def,
73   false, /* checked */
74   true   /* perpetual */
75 };
76 
77 PFS_engine_table*
create(void)78 table_variables_by_thread::create(void)
79 {
80   return new table_variables_by_thread();
81 }
82 
get_row_count(void)83 ha_rows table_variables_by_thread::get_row_count(void)
84 {
85   mysql_mutex_lock(&LOCK_plugin_delete);
86   mysql_rwlock_rdlock(&LOCK_system_variables_hash);
87   ulong system_var_count= get_system_variable_hash_records();
88   mysql_rwlock_unlock(&LOCK_system_variables_hash);
89   mysql_mutex_unlock(&LOCK_plugin_delete);
90   return (global_thread_container.get_row_count() * system_var_count);
91 }
92 
table_variables_by_thread()93 table_variables_by_thread::table_variables_by_thread()
94   : PFS_engine_table(&m_share, &m_pos),
95     m_sysvar_cache(true), m_row_exists(false), m_pos(), m_next_pos(), m_context(NULL)
96 {}
97 
reset_position(void)98 void table_variables_by_thread::reset_position(void)
99 {
100   m_pos.reset();
101   m_next_pos.reset();
102 }
103 
rnd_init(bool scan)104 int table_variables_by_thread::rnd_init(bool scan)
105 {
106   /*
107     Build array of SHOW_VARs from system variable hash prior to materializing
108     threads in rnd_next() or rnd_pos().
109   */
110   m_sysvar_cache.initialize_session();
111 
112   /* Record the version of the system variable hash. */
113   ulonglong hash_version= m_sysvar_cache.get_sysvar_hash_version();
114 
115   /*
116     The table context holds the current version of the system variable hash and
117     a record of which threads were materialized.
118     If scan == true, then allocate a new context from mem_root and store in TLS.
119     If scan == false, then restore from TLS.
120   */
121   m_context= (table_variables_by_thread_context *)current_thd->alloc(sizeof(table_variables_by_thread_context));
122   new(m_context) table_variables_by_thread_context(hash_version, !scan);
123   return 0;
124 }
125 
rnd_next(void)126 int table_variables_by_thread::rnd_next(void)
127 {
128   /* If system variable hash changes, exit with warning. */ // TODO: Issue warning
129   if (!m_context->versions_match())
130     return HA_ERR_END_OF_FILE;
131 
132   bool has_more_thread= true;
133 
134   for (m_pos.set_at(&m_next_pos);
135        has_more_thread;
136        m_pos.next_thread())
137   {
138     PFS_thread *pfs_thread= global_thread_container.get(m_pos.m_index_1, &has_more_thread);
139 
140     /* Materialize all variables for the current thread. Assign a dedicated mem_root. */
141     if (m_sysvar_cache.materialize_session(pfs_thread, true) == 0)
142     {
143       /* Mark this thread as materialized. */
144       m_context->set_item(m_pos.m_index_1);
145 
146       const System_variable *system_var= m_sysvar_cache.get(m_pos.m_index_2);
147       if (system_var != NULL)
148       {
149         make_row(pfs_thread, system_var);
150         m_next_pos.set_after(&m_pos);
151         return 0;
152       }
153     }
154   }
155   return HA_ERR_END_OF_FILE;
156 }
157 
158 int
rnd_pos(const void * pos)159 table_variables_by_thread::rnd_pos(const void *pos)
160 {
161   /* If system variable hash changes, do nothing. */
162   if (!m_context->versions_match())
163     return HA_ERR_RECORD_DELETED;
164 
165   set_position(pos);
166   assert(m_pos.m_index_1 < global_thread_container.get_row_count());
167 
168   PFS_thread *pfs_thread= global_thread_container.get(m_pos.m_index_1);
169     /*
170     Only materialize threads that were previously materialized by rnd_next().
171     If a thread cannot be rematerialized, then do nothing.
172     Only materialize the requested system variable to avoid repeated
173     materialization of each thread, such as with ORDER BY variable_name.
174   */
175   if (m_context->is_item_set(m_pos.m_index_1) &&
176       /* Materialize only the requested variable. */
177       m_sysvar_cache.materialize_session(pfs_thread, m_pos.m_index_2) == 0)
178   {
179     /* Get the first (and only) element from the cache. */
180     const System_variable *system_var= m_sysvar_cache.get();
181     if (system_var != NULL)
182     {
183       make_row(pfs_thread, system_var);
184       m_next_pos.set_after(&m_pos);
185       return 0;
186     }
187   }
188   return HA_ERR_RECORD_DELETED;
189 }
190 
191 void table_variables_by_thread
make_row(PFS_thread * thread,const System_variable * system_var)192 ::make_row(PFS_thread *thread, const System_variable *system_var)
193 {
194   pfs_optimistic_state lock;
195   m_row_exists= false;
196   if (system_var->is_null() || system_var->is_ignored())
197     return;
198 
199   /* Protect this reader against a thread termination */
200   thread->m_lock.begin_optimistic_lock(&lock);
201 
202   m_row.m_thread_internal_id= thread->m_thread_internal_id;
203   m_row.m_variable_name.make_row(system_var->m_name, system_var->m_name_length);
204   m_row.m_variable_value.make_row(system_var);
205 
206   if (!thread->m_lock.end_optimistic_lock(&lock))
207     return;
208 
209   m_row_exists= true;
210 }
211 
212 int table_variables_by_thread
read_row_values(TABLE * table,unsigned char * buf,Field ** fields,bool read_all)213 ::read_row_values(TABLE *table,
214                   unsigned char *buf,
215                   Field **fields,
216                   bool read_all)
217 {
218   Field *f;
219 
220   if (unlikely(! m_row_exists))
221     return HA_ERR_RECORD_DELETED;
222 
223   /* Set the null bits */
224   assert(table->s->null_bytes == 1);
225   buf[0]= 0;
226 
227   for (; (f= *fields) ; fields++)
228   {
229     if (read_all || bitmap_is_set(table->read_set, f->field_index))
230     {
231       switch(f->field_index)
232       {
233       case 0: /* THREAD_ID */
234         set_field_ulonglong(f, m_row.m_thread_internal_id);
235         break;
236       case 1: /* VARIABLE_NAME */
237         set_field_varchar_utf8(f, m_row.m_variable_name.m_str, m_row.m_variable_name.m_length);
238         break;
239       case 2: /* VARIABLE_VALUE */
240         m_row.m_variable_value.set_field(f);
241         break;
242       default:
243         assert(false);
244       }
245     }
246   }
247 
248   return 0;
249 }
250 
251