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