1 /* Copyright (c) 2008, 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 Foundation,
21   51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22 
23 #include "my_global.h"
24 #include "my_thread.h"
25 #include "table_threads.h"
26 #include "sql_parse.h"
27 #include "pfs_instr_class.h"
28 #include "pfs_instr.h"
29 
30 THR_LOCK table_threads::m_table_lock;
31 
32 static const TABLE_FIELD_TYPE field_types[]=
33 {
34   {
35     { C_STRING_WITH_LEN("THREAD_ID") },
36     { C_STRING_WITH_LEN("bigint(20)") },
37     { NULL, 0}
38   },
39   {
40     { C_STRING_WITH_LEN("NAME") },
41     { C_STRING_WITH_LEN("varchar(128)") },
42     { NULL, 0}
43   },
44   {
45     { C_STRING_WITH_LEN("TYPE") },
46     { C_STRING_WITH_LEN("varchar(10)") },
47     { NULL, 0}
48   },
49   {
50     { C_STRING_WITH_LEN("PROCESSLIST_ID") },
51     { C_STRING_WITH_LEN("bigint(20)") },
52     { NULL, 0}
53   },
54   {
55     { C_STRING_WITH_LEN("PROCESSLIST_USER") },
56     { C_STRING_WITH_LEN("varchar(" USERNAME_CHAR_LENGTH_STR ")") },
57     { NULL, 0}
58   },
59   {
60     { C_STRING_WITH_LEN("PROCESSLIST_HOST") },
61     { C_STRING_WITH_LEN("varchar(60)") },
62     { NULL, 0}
63   },
64   {
65     { C_STRING_WITH_LEN("PROCESSLIST_DB") },
66     { C_STRING_WITH_LEN("varchar(64)") },
67     { NULL, 0}
68   },
69   {
70     { C_STRING_WITH_LEN("PROCESSLIST_COMMAND") },
71     { C_STRING_WITH_LEN("varchar(16)") },
72     { NULL, 0}
73   },
74   {
75     { C_STRING_WITH_LEN("PROCESSLIST_TIME") },
76     { C_STRING_WITH_LEN("bigint(20)") },
77     { NULL, 0}
78   },
79   {
80     { C_STRING_WITH_LEN("PROCESSLIST_STATE") },
81     { C_STRING_WITH_LEN("varchar(64)") },
82     { NULL, 0}
83   },
84   {
85     { C_STRING_WITH_LEN("PROCESSLIST_INFO") },
86     { C_STRING_WITH_LEN("longtext") },
87     { NULL, 0}
88   },
89   {
90     { C_STRING_WITH_LEN("PARENT_THREAD_ID") },
91     { C_STRING_WITH_LEN("bigint(20)") },
92     { NULL, 0}
93   },
94   {
95     { C_STRING_WITH_LEN("ROLE") },
96     { C_STRING_WITH_LEN("varchar(64)") },
97     { NULL, 0}
98   },
99   {
100     { C_STRING_WITH_LEN("INSTRUMENTED") },
101     { C_STRING_WITH_LEN("enum(\'YES\',\'NO\')") },
102     { NULL, 0}
103   },
104   {
105     { C_STRING_WITH_LEN("HISTORY") },
106     { C_STRING_WITH_LEN("enum(\'YES\',\'NO\')") },
107     { NULL, 0}
108   },
109   {
110     { C_STRING_WITH_LEN("CONNECTION_TYPE") },
111     { C_STRING_WITH_LEN("varchar(16)") },
112     { NULL, 0 }
113   },
114   {
115     { C_STRING_WITH_LEN("THREAD_OS_ID") },
116     { C_STRING_WITH_LEN("bigint(20)") },
117     { NULL, 0}
118   },
119 };
120 
121 TABLE_FIELD_DEF
122 table_threads::m_field_def=
123 { 17, field_types };
124 
125 PFS_engine_table_share
126 table_threads::m_share=
127 {
128   { C_STRING_WITH_LEN("threads") },
129   &pfs_updatable_acl,
130   table_threads::create,
131   NULL, /* write_row */
132   NULL, /* delete_all_rows */
133   cursor_by_thread::get_row_count,
134   sizeof(PFS_simple_index), /* ref length */
135   &m_table_lock,
136   &m_field_def,
137   false, /* checked */
138   false  /* perpetual */
139 };
140 
create()141 PFS_engine_table* table_threads::create()
142 {
143   return new table_threads();
144 }
145 
table_threads()146 table_threads::table_threads()
147   : cursor_by_thread(& m_share),
148   m_row_exists(false)
149 {}
150 
make_row(PFS_thread * pfs)151 void table_threads::make_row(PFS_thread *pfs)
152 {
153   pfs_optimistic_state lock;
154   pfs_optimistic_state session_lock;
155   pfs_optimistic_state stmt_lock;
156   PFS_stage_class *stage_class;
157   PFS_thread_class *safe_class;
158 
159   m_row_exists= false;
160 
161   /* Protect this reader against thread termination */
162   pfs->m_lock.begin_optimistic_lock(&lock);
163 
164   safe_class= sanitize_thread_class(pfs->m_class);
165   if (unlikely(safe_class == NULL))
166     return;
167 
168   m_row.m_thread_internal_id= pfs->m_thread_internal_id;
169   m_row.m_parent_thread_internal_id= pfs->m_parent_thread_internal_id;
170   m_row.m_processlist_id= pfs->m_processlist_id;
171   m_row.m_thread_os_id= pfs->m_thread_os_id;
172   m_row.m_name= safe_class->m_name;
173   m_row.m_name_length= safe_class->m_name_length;
174 
175   /* Protect this reader against session attribute changes */
176   pfs->m_session_lock.begin_optimistic_lock(&session_lock);
177 
178   m_row.m_username_length= pfs->m_username_length;
179   if (unlikely(m_row.m_username_length > sizeof(m_row.m_username)))
180     return;
181   if (m_row.m_username_length != 0)
182     memcpy(m_row.m_username, pfs->m_username, m_row.m_username_length);
183 
184   m_row.m_hostname_length= pfs->m_hostname_length;
185   if (unlikely(m_row.m_hostname_length > sizeof(m_row.m_hostname)))
186     return;
187   if (m_row.m_hostname_length != 0)
188     memcpy(m_row.m_hostname, pfs->m_hostname, m_row.m_hostname_length);
189 
190   if (! pfs->m_session_lock.end_optimistic_lock(& session_lock))
191   {
192     /*
193       One of the columns:
194       - PROCESSLIST_USER
195       - PROCESSLIST_HOST
196       is being updated.
197       Do not discard the entire row.
198       Do not loop waiting for a stable value.
199       Just return NULL values.
200     */
201     m_row.m_username_length= 0;
202     m_row.m_hostname_length= 0;
203   }
204 
205   /* Protect this reader against statement attributes changes */
206   pfs->m_stmt_lock.begin_optimistic_lock(&stmt_lock);
207 
208   m_row.m_dbname_length= pfs->m_dbname_length;
209   if (unlikely(m_row.m_dbname_length > sizeof(m_row.m_dbname)))
210     return;
211   if (m_row.m_dbname_length != 0)
212     memcpy(m_row.m_dbname, pfs->m_dbname, m_row.m_dbname_length);
213 
214   m_row.m_processlist_info_ptr= & pfs->m_processlist_info[0];
215   m_row.m_processlist_info_length= pfs->m_processlist_info_length;
216 
217   if (! pfs->m_stmt_lock.end_optimistic_lock(& stmt_lock))
218   {
219     /*
220       One of the columns:
221       - PROCESSLIST_DB
222       - PROCESSLIST_INFO
223       is being updated.
224       Do not discard the entire row.
225       Do not loop waiting for a stable value.
226       Just return NULL values.
227     */
228     m_row.m_dbname_length= 0;
229     m_row.m_processlist_info_length= 0;
230   }
231 
232   /* Dirty read, sanitize the command. */
233   m_row.m_command= pfs->m_command;
234   if ((m_row.m_command < 0) || (m_row.m_command > COM_END))
235     m_row.m_command= COM_END;
236 
237   m_row.m_start_time= pfs->m_start_time;
238 
239   stage_class= find_stage_class(pfs->m_stage);
240   if (stage_class != NULL)
241   {
242     m_row.m_processlist_state_ptr= stage_class->m_name + stage_class->m_prefix_length;
243     m_row.m_processlist_state_length= stage_class->m_name_length - stage_class->m_prefix_length;
244   }
245   else
246   {
247     m_row.m_processlist_state_length= 0;
248   }
249   m_row.m_connection_type = pfs->m_connection_type;
250 
251 
252   m_row.m_enabled= !pfs->m_disable_instrumentation && pfs->m_enabled;
253   m_row.m_history= pfs->m_history;
254   m_row.m_psi= pfs;
255 
256   if (pfs->m_lock.end_optimistic_lock(& lock))
257     m_row_exists= true;
258 
259   if(pfs->m_disable_instrumentation)
260     m_row_exists= false;
261 }
262 
read_row_values(TABLE * table,unsigned char * buf,Field ** fields,bool read_all)263 int table_threads::read_row_values(TABLE *table,
264                                    unsigned char *buf,
265                                    Field **fields,
266                                    bool read_all)
267 {
268   Field *f;
269   const char *str= NULL;
270   int len= 0;
271 
272   if (unlikely(! m_row_exists))
273     return HA_ERR_RECORD_DELETED;
274 
275   /* Set the null bits */
276   assert(table->s->null_bytes == 2);
277   buf[0]= 0;
278   buf[1]= 0;
279 
280   for (; (f= *fields) ; fields++)
281   {
282     if (read_all || bitmap_is_set(table->read_set, f->field_index))
283     {
284       switch(f->field_index)
285       {
286       case 0: /* THREAD_ID */
287         set_field_ulonglong(f, m_row.m_thread_internal_id);
288         break;
289       case 1: /* NAME */
290         set_field_varchar_utf8(f, m_row.m_name, m_row.m_name_length);
291         break;
292       case 2: /* TYPE */
293         if (m_row.m_processlist_id != 0)
294           set_field_varchar_utf8(f, "FOREGROUND", 10);
295         else
296           set_field_varchar_utf8(f, "BACKGROUND", 10);
297         break;
298       case 3: /* PROCESSLIST_ID */
299         if (m_row.m_processlist_id != 0)
300           set_field_ulonglong(f, m_row.m_processlist_id);
301         else
302           f->set_null();
303         break;
304       case 4: /* PROCESSLIST_USER */
305         if (m_row.m_username_length > 0)
306           set_field_varchar_utf8(f, m_row.m_username,
307                                  m_row.m_username_length);
308         else
309           f->set_null();
310         break;
311       case 5: /* PROCESSLIST_HOST */
312         if (m_row.m_hostname_length > 0)
313           set_field_varchar_utf8(f, m_row.m_hostname,
314                                  m_row.m_hostname_length);
315         else
316           f->set_null();
317         break;
318       case 6: /* PROCESSLIST_DB */
319         if (m_row.m_dbname_length > 0)
320           set_field_varchar_utf8(f, m_row.m_dbname,
321                                  m_row.m_dbname_length);
322         else
323           f->set_null();
324         break;
325       case 7: /* PROCESSLIST_COMMAND */
326         if (m_row.m_processlist_id != 0)
327           set_field_varchar_utf8(f, command_name[m_row.m_command].str,
328                                  command_name[m_row.m_command].length);
329         else
330           f->set_null();
331         break;
332       case 8: /* PROCESSLIST_TIME */
333         if (m_row.m_start_time)
334         {
335           time_t now= my_time(0);
336           ulonglong elapsed= (now > m_row.m_start_time ? now - m_row.m_start_time : 0);
337           set_field_ulonglong(f, elapsed);
338         }
339         else
340           f->set_null();
341         break;
342       case 9: /* PROCESSLIST_STATE */
343         /* This column's datatype is declared as varchar(64). Thread's state
344            message cannot be more than 64 characters. Otherwise, we will end up
345            in 'data truncated' warning/error (depends sql_mode setting) when
346            server is updating this column for those threads. To prevent this
347            kind of issue, an assert is added.
348          */
349         assert(m_row.m_processlist_state_length <= f->char_length());
350         if (m_row.m_processlist_state_length > 0)
351           set_field_varchar_utf8(f, m_row.m_processlist_state_ptr,
352                                  m_row.m_processlist_state_length);
353         else
354           f->set_null();
355         break;
356       case 10: /* PROCESSLIST_INFO */
357         if (m_row.m_processlist_info_length > 0)
358           set_field_longtext_utf8(f, m_row.m_processlist_info_ptr,
359                                   m_row.m_processlist_info_length);
360         else
361           f->set_null();
362         break;
363       case 11: /* PARENT_THREAD_ID */
364         if (m_row.m_parent_thread_internal_id != 0)
365           set_field_ulonglong(f, m_row.m_parent_thread_internal_id);
366         else
367           f->set_null();
368         break;
369       case 12: /* ROLE */
370         f->set_null();
371         break;
372       case 13: /* INSTRUMENTED */
373         set_field_enum(f, m_row.m_enabled ? ENUM_YES : ENUM_NO);
374         break;
375       case 14: /* HISTORY */
376         set_field_enum(f, m_row.m_history ? ENUM_YES : ENUM_NO);
377         break;
378       case 15: /* CONNECTION_TYPE */
379         get_vio_type_name(m_row.m_connection_type, & str, & len);
380         if (len > 0)
381           set_field_varchar_utf8(f, str, len);
382         else
383           f->set_null();
384         break;
385       case 16: /* THREAD_OS_ID */
386         if (m_row.m_thread_os_id > 0)
387           set_field_ulonglong(f, m_row.m_thread_os_id);
388         else
389           f->set_null();
390         break;
391       default:
392         assert(false);
393       }
394     }
395   }
396   return 0;
397 }
398 
update_row_values(TABLE * table,const unsigned char * old_buf,unsigned char * new_buf,Field ** fields)399 int table_threads::update_row_values(TABLE *table,
400                                      const unsigned char *old_buf,
401                                      unsigned char *new_buf,
402                                      Field **fields)
403 {
404   Field *f;
405   enum_yes_no value;
406 
407   for (; (f= *fields) ; fields++)
408   {
409     if (bitmap_is_set(table->write_set, f->field_index))
410     {
411       switch(f->field_index)
412       {
413       case 0: /* THREAD_ID */
414       case 1: /* NAME */
415       case 2: /* TYPE */
416       case 3: /* PROCESSLIST_ID */
417       case 4: /* PROCESSLIST_USER */
418       case 5: /* PROCESSLIST_HOST */
419       case 6: /* PROCESSLIST_DB */
420       case 7: /* PROCESSLIST_COMMAND */
421       case 8: /* PROCESSLIST_TIME */
422       case 9: /* PROCESSLIST_STATE */
423       case 10: /* PROCESSLIST_INFO */
424       case 11: /* PARENT_THREAD_ID */
425       case 12: /* ROLE */
426         return HA_ERR_WRONG_COMMAND;
427       case 13: /* INSTRUMENTED */
428         value= (enum_yes_no) get_field_enum(f);
429         m_row.m_psi->set_enabled((value == ENUM_YES) ? true : false);
430         break;
431       case 14: /* HISTORY */
432         value= (enum_yes_no) get_field_enum(f);
433         m_row.m_psi->set_history((value == ENUM_YES) ? true : false);
434         break;
435       case 15: /* CONNECTION_TYPE */
436       case 16: /* THREAD_OS_ID */
437         return HA_ERR_WRONG_COMMAND;
438       default:
439         assert(false);
440       }
441     }
442   }
443   return 0;
444 }
445 
446