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_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 
read_row_values(TABLE * table,unsigned char * buf,Field ** fields,bool read_all)260 int table_threads::read_row_values(TABLE *table,
261                                    unsigned char *buf,
262                                    Field **fields,
263                                    bool read_all)
264 {
265   Field *f;
266   const char *str= NULL;
267   int len= 0;
268 
269   if (unlikely(! m_row_exists))
270     return HA_ERR_RECORD_DELETED;
271 
272   /* Set the null bits */
273   assert(table->s->null_bytes == 2);
274   buf[0]= 0;
275   buf[1]= 0;
276 
277   for (; (f= *fields) ; fields++)
278   {
279     if (read_all || bitmap_is_set(table->read_set, f->field_index))
280     {
281       switch(f->field_index)
282       {
283       case 0: /* THREAD_ID */
284         set_field_ulonglong(f, m_row.m_thread_internal_id);
285         break;
286       case 1: /* NAME */
287         set_field_varchar_utf8(f, m_row.m_name, m_row.m_name_length);
288         break;
289       case 2: /* TYPE */
290         if (m_row.m_processlist_id != 0)
291           set_field_varchar_utf8(f, "FOREGROUND", 10);
292         else
293           set_field_varchar_utf8(f, "BACKGROUND", 10);
294         break;
295       case 3: /* PROCESSLIST_ID */
296         if (m_row.m_processlist_id != 0)
297           set_field_ulonglong(f, m_row.m_processlist_id);
298         else
299           f->set_null();
300         break;
301       case 4: /* PROCESSLIST_USER */
302         if (m_row.m_username_length > 0)
303           set_field_varchar_utf8(f, m_row.m_username,
304                                  m_row.m_username_length);
305         else
306           f->set_null();
307         break;
308       case 5: /* PROCESSLIST_HOST */
309         if (m_row.m_hostname_length > 0)
310           set_field_varchar_utf8(f, m_row.m_hostname,
311                                  m_row.m_hostname_length);
312         else
313           f->set_null();
314         break;
315       case 6: /* PROCESSLIST_DB */
316         if (m_row.m_dbname_length > 0)
317           set_field_varchar_utf8(f, m_row.m_dbname,
318                                  m_row.m_dbname_length);
319         else
320           f->set_null();
321         break;
322       case 7: /* PROCESSLIST_COMMAND */
323         if (m_row.m_processlist_id != 0)
324           set_field_varchar_utf8(f, command_name[m_row.m_command].str,
325                                  command_name[m_row.m_command].length);
326         else
327           f->set_null();
328         break;
329       case 8: /* PROCESSLIST_TIME */
330         if (m_row.m_start_time)
331         {
332           time_t now= my_time(0);
333           ulonglong elapsed= (now > m_row.m_start_time ? now - m_row.m_start_time : 0);
334           set_field_ulonglong(f, elapsed);
335         }
336         else
337           f->set_null();
338         break;
339       case 9: /* PROCESSLIST_STATE */
340         /* This column's datatype is declared as varchar(64). Thread's state
341            message cannot be more than 64 characters. Otherwise, we will end up
342            in 'data truncated' warning/error (depends sql_mode setting) when
343            server is updating this column for those threads. To prevent this
344            kind of issue, an assert is added.
345          */
346         assert(m_row.m_processlist_state_length <= f->char_length());
347         if (m_row.m_processlist_state_length > 0)
348           set_field_varchar_utf8(f, m_row.m_processlist_state_ptr,
349                                  m_row.m_processlist_state_length);
350         else
351           f->set_null();
352         break;
353       case 10: /* PROCESSLIST_INFO */
354         if (m_row.m_processlist_info_length > 0)
355           set_field_longtext_utf8(f, m_row.m_processlist_info_ptr,
356                                   m_row.m_processlist_info_length);
357         else
358           f->set_null();
359         break;
360       case 11: /* PARENT_THREAD_ID */
361         if (m_row.m_parent_thread_internal_id != 0)
362           set_field_ulonglong(f, m_row.m_parent_thread_internal_id);
363         else
364           f->set_null();
365         break;
366       case 12: /* ROLE */
367         f->set_null();
368         break;
369       case 13: /* INSTRUMENTED */
370         set_field_enum(f, m_row.m_enabled ? ENUM_YES : ENUM_NO);
371         break;
372       case 14: /* HISTORY */
373         set_field_enum(f, m_row.m_history ? ENUM_YES : ENUM_NO);
374         break;
375       case 15: /* CONNECTION_TYPE */
376         get_vio_type_name(m_row.m_connection_type, & str, & len);
377         if (len > 0)
378           set_field_varchar_utf8(f, str, len);
379         else
380           f->set_null();
381         break;
382       case 16: /* THREAD_OS_ID */
383         if (m_row.m_thread_os_id > 0)
384           set_field_ulonglong(f, m_row.m_thread_os_id);
385         else
386           f->set_null();
387         break;
388       default:
389         assert(false);
390       }
391     }
392   }
393   return 0;
394 }
395 
update_row_values(TABLE * table,const unsigned char * old_buf,unsigned char * new_buf,Field ** fields)396 int table_threads::update_row_values(TABLE *table,
397                                      const unsigned char *old_buf,
398                                      unsigned char *new_buf,
399                                      Field **fields)
400 {
401   Field *f;
402   enum_yes_no value;
403 
404   for (; (f= *fields) ; fields++)
405   {
406     if (bitmap_is_set(table->write_set, f->field_index))
407     {
408       switch(f->field_index)
409       {
410       case 0: /* THREAD_ID */
411       case 1: /* NAME */
412       case 2: /* TYPE */
413       case 3: /* PROCESSLIST_ID */
414       case 4: /* PROCESSLIST_USER */
415       case 5: /* PROCESSLIST_HOST */
416       case 6: /* PROCESSLIST_DB */
417       case 7: /* PROCESSLIST_COMMAND */
418       case 8: /* PROCESSLIST_TIME */
419       case 9: /* PROCESSLIST_STATE */
420       case 10: /* PROCESSLIST_INFO */
421       case 11: /* PARENT_THREAD_ID */
422       case 12: /* ROLE */
423         return HA_ERR_WRONG_COMMAND;
424       case 13: /* INSTRUMENTED */
425         value= (enum_yes_no) get_field_enum(f);
426         m_row.m_psi->set_enabled((value == ENUM_YES) ? true : false);
427         break;
428       case 14: /* HISTORY */
429         value= (enum_yes_no) get_field_enum(f);
430         m_row.m_psi->set_history((value == ENUM_YES) ? true : false);
431         break;
432       case 15: /* CONNECTION_TYPE */
433       case 16: /* THREAD_OS_ID */
434         return HA_ERR_WRONG_COMMAND;
435       default:
436         assert(false);
437       }
438     }
439   }
440   return 0;
441 }
442 
443