1 /* Copyright (c) 2013, 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_uvar_by_thread.cc
25   Table USER_VARIABLES_BY_THREAD (implementation).
26 */
27 
28 #include "my_global.h"
29 #include "my_thread.h"
30 #include "pfs_instr_class.h"
31 #include "pfs_column_types.h"
32 #include "pfs_column_values.h"
33 #include "table_uvar_by_thread.h"
34 #include "pfs_global.h"
35 #include "pfs_visitor.h"
36 #include "pfs_buffer_container.h"
37 
38 /* Iteration on THD from the sql layer. */
39 #include "sql_class.h"
40 #include "mysqld_thd_manager.h"
41 
42 class Find_thd_user_var : public Find_THD_Impl
43 {
44 public:
Find_thd_user_var(THD * unsafe_thd)45   Find_thd_user_var(THD *unsafe_thd)
46     : m_unsafe_thd(unsafe_thd)
47   {}
48 
operator ()(THD * thd)49   virtual bool operator()(THD *thd)
50   {
51     if (thd != m_unsafe_thd)
52       return false;
53 
54     if (thd->user_vars.records == 0)
55       return false;
56 
57     mysql_mutex_lock(&thd->LOCK_thd_data);
58     return true;
59   }
60 
61 private:
62   THD *m_unsafe_thd;
63 };
64 
materialize(PFS_thread * pfs,THD * thd)65 void User_variables::materialize(PFS_thread *pfs, THD *thd)
66 {
67   reset();
68 
69   m_pfs= pfs;
70   m_thread_internal_id= pfs->m_thread_internal_id;
71   m_array.reserve(thd->user_vars.records);
72 
73   user_var_entry *sql_uvar;
74 
75   uint index= 0;
76   User_variable empty;
77 
78   /* Protects thd->user_vars. */
79   mysql_mutex_assert_owner(&thd->LOCK_thd_data);
80 
81   for (;;)
82   {
83     sql_uvar= reinterpret_cast<user_var_entry*> (my_hash_element(& thd->user_vars, index));
84     if (sql_uvar == NULL)
85       break;
86 
87     /*
88       m_array is a container of objects (not pointers)
89 
90       Naive code can:
91       - build locally a new entry
92       - add it to the container
93       but this causes useless object construction, destruction, and deep copies.
94 
95       What we do here:
96       - add a dummy (empty) entry
97       - the container does a deep copy on something empty,
98         so that there is nothing to copy.
99       - get a reference to the entry added in the container
100       - complete -- in place -- the entry initialization
101     */
102     m_array.push_back(empty);
103     User_variable & pfs_uvar= m_array.back();
104 
105     /* Copy VARIABLE_NAME */
106     const char *name= sql_uvar->entry_name.ptr();
107     size_t name_length= sql_uvar->entry_name.length();
108     assert(name_length <= sizeof(pfs_uvar.m_name));
109     pfs_uvar.m_name.make_row(name, name_length);
110 
111     /* Copy VARIABLE_VALUE */
112     my_bool null_value;
113     String *str_value;
114     String str_buffer;
115     uint decimals= 0;
116     str_value= sql_uvar->val_str(& null_value, & str_buffer, decimals);
117     if (str_value != NULL)
118     {
119       pfs_uvar.m_value.make_row(str_value->ptr(), str_value->length());
120     }
121     else
122     {
123       pfs_uvar.m_value.make_row(NULL, 0);
124     }
125 
126     index++;
127   }
128 }
129 
130 THR_LOCK table_uvar_by_thread::m_table_lock;
131 
132 static const TABLE_FIELD_TYPE field_types[]=
133 {
134   {
135     { C_STRING_WITH_LEN("THREAD_ID") },
136     { C_STRING_WITH_LEN("bigint(20)") },
137     { NULL, 0}
138   },
139   {
140     { C_STRING_WITH_LEN("VARIABLE_NAME") },
141     { C_STRING_WITH_LEN("varchar(64)") },
142     { NULL, 0}
143   },
144   {
145     { C_STRING_WITH_LEN("VARIABLE_VALUE") },
146     { C_STRING_WITH_LEN("longblob") },
147     { NULL, 0}
148   }
149 };
150 
151 TABLE_FIELD_DEF
152 table_uvar_by_thread::m_field_def=
153 { 3, field_types };
154 
155 PFS_engine_table_share
156 table_uvar_by_thread::m_share=
157 {
158   { C_STRING_WITH_LEN("user_variables_by_thread") },
159   &pfs_readonly_acl,
160   table_uvar_by_thread::create,
161   NULL, /* write_row */
162   NULL, /* delete_all_rows */
163   table_uvar_by_thread::get_row_count,
164   sizeof(pos_t),
165   &m_table_lock,
166   &m_field_def,
167   false, /* checked */
168   false  /* perpetual */
169 };
170 
171 PFS_engine_table*
create(void)172 table_uvar_by_thread::create(void)
173 {
174   return new table_uvar_by_thread();
175 }
176 
177 ha_rows
get_row_count(void)178 table_uvar_by_thread::get_row_count(void)
179 {
180   /*
181     This is an estimate only, not a hard limit.
182     The row count is given as a multiple of thread_max,
183     so that a join between:
184     - table performance_schema.threads
185     - table performance_schema.user_variables_by_thread
186     will still evaluate relative table sizes correctly
187     when deciding a join order.
188   */
189   return global_thread_container.get_row_count() * 10;
190 }
191 
table_uvar_by_thread()192 table_uvar_by_thread::table_uvar_by_thread()
193   : PFS_engine_table(&m_share, &m_pos),
194     m_row_exists(false), m_pos(), m_next_pos()
195 {}
196 
reset_position(void)197 void table_uvar_by_thread::reset_position(void)
198 {
199   m_pos.reset();
200   m_next_pos.reset();
201 }
202 
rnd_next(void)203 int table_uvar_by_thread::rnd_next(void)
204 {
205   PFS_thread *thread;
206   bool has_more_thread= true;
207 
208   for (m_pos.set_at(&m_next_pos);
209        has_more_thread;
210        m_pos.next_thread())
211   {
212     thread= global_thread_container.get(m_pos.m_index_1, & has_more_thread);
213     if (thread != NULL)
214     {
215       if (materialize(thread) == 0)
216       {
217         const User_variable *uvar= m_THD_cache.get(m_pos.m_index_2);
218         if (uvar != NULL)
219         {
220           make_row(thread, uvar);
221           m_next_pos.set_after(&m_pos);
222           return 0;
223         }
224       }
225     }
226   }
227 
228   return HA_ERR_END_OF_FILE;
229 }
230 
231 int
rnd_pos(const void * pos)232 table_uvar_by_thread::rnd_pos(const void *pos)
233 {
234   PFS_thread *thread;
235 
236   set_position(pos);
237 
238   thread= global_thread_container.get(m_pos.m_index_1);
239   if (thread != NULL)
240   {
241     if (materialize(thread) == 0)
242     {
243       const User_variable *uvar= m_THD_cache.get(m_pos.m_index_2);
244       if (uvar != NULL)
245       {
246         make_row(thread, uvar);
247         return 0;
248       }
249     }
250   }
251 
252   return HA_ERR_RECORD_DELETED;
253 }
254 
materialize(PFS_thread * thread)255 int table_uvar_by_thread::materialize(PFS_thread *thread)
256 {
257   if (m_THD_cache.is_materialized(thread))
258     return 0;
259 
260   if (! thread->m_lock.is_populated())
261     return 1;
262 
263   THD *unsafe_thd= thread->m_thd;
264   if (unsafe_thd == NULL)
265     return 1;
266 
267   Find_thd_user_var finder(unsafe_thd);
268   THD *safe_thd= Global_THD_manager::get_instance()->find_thd(&finder);
269   if (safe_thd == NULL)
270     return 1;
271 
272   m_THD_cache.materialize(thread, safe_thd);
273   mysql_mutex_unlock(&safe_thd->LOCK_thd_data);
274   return 0;
275 }
276 
277 void table_uvar_by_thread
make_row(PFS_thread * thread,const User_variable * uvar)278 ::make_row(PFS_thread *thread, const User_variable *uvar)
279 {
280   pfs_optimistic_state lock;
281   m_row_exists= false;
282 
283   /* Protect this reader against a thread termination */
284   thread->m_lock.begin_optimistic_lock(&lock);
285 
286   m_row.m_thread_internal_id= thread->m_thread_internal_id;
287 
288   /* uvar is materialized, pointing to it directly. */
289   m_row.m_variable_name= & uvar->m_name;
290   m_row.m_variable_value= & uvar->m_value;
291 
292   if (! thread->m_lock.end_optimistic_lock(&lock))
293     return;
294 
295   m_row_exists= true;
296 }
297 
298 int table_uvar_by_thread
read_row_values(TABLE * table,unsigned char * buf,Field ** fields,bool read_all)299 ::read_row_values(TABLE *table,
300                   unsigned char *buf,
301                   Field **fields,
302                   bool read_all)
303 {
304   Field *f;
305 
306   if (unlikely(! m_row_exists))
307     return HA_ERR_RECORD_DELETED;
308 
309   /* Set the null bits */
310   assert(table->s->null_bytes == 1);
311   buf[0]= 0;
312 
313   assert(m_row.m_variable_name != NULL);
314   assert(m_row.m_variable_value != NULL);
315 
316   for (; (f= *fields) ; fields++)
317   {
318     if (read_all || bitmap_is_set(table->read_set, f->field_index))
319     {
320       switch(f->field_index)
321       {
322       case 0: /* THREAD_ID */
323         set_field_ulonglong(f, m_row.m_thread_internal_id);
324         break;
325       case 1: /* VARIABLE_NAME */
326         set_field_varchar_utf8(f,
327                                m_row.m_variable_name->m_str,
328                                m_row.m_variable_name->m_length);
329         break;
330       case 2: /* VARIABLE_VALUE */
331         if (m_row.m_variable_value->get_value_length() > 0)
332         {
333           set_field_blob(f,
334                          m_row.m_variable_value->get_value(),
335                          m_row.m_variable_value->get_value_length());
336         }
337         else
338         {
339           f->set_null();
340         }
341         break;
342       default:
343         assert(false);
344       }
345     }
346   }
347 
348   return 0;
349 }
350 
351