1/* Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
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 as published by
5 the Free Software Foundation; version 2 of the License.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software Foundation,
14 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
15
16#include "my_global.h"
17#include "my_pthread.h"
18#include "table_threads.h"
19#include "sql_parse.h"
20#include "pfs_instr_class.h"
21#include "pfs_instr.h"
22
23THR_LOCK table_threads::m_table_lock;
24
25PFS_engine_table_share
26table_threads::m_share=
27{
28 { C_STRING_WITH_LEN("threads") },
29 &pfs_updatable_acl,
30 &table_threads::create,
31 NULL, /* write_row */
32 NULL, /* delete_all_rows */
33 NULL, /* get_row_count */
34 1000, /* records */
35 sizeof(PFS_simple_index), /* ref length */
36 &m_table_lock,
37 { C_STRING_WITH_LEN("CREATE TABLE threads("
38 "THREAD_ID BIGINT unsigned not null,"
39 "NAME VARCHAR(128) not null,"
40 "TYPE VARCHAR(10) not null,"
41 "PROCESSLIST_ID BIGINT unsigned,"
42 "PROCESSLIST_USER VARCHAR(16),"
43 "PROCESSLIST_HOST VARCHAR(60),"
44 "PROCESSLIST_DB VARCHAR(64),"
45 "PROCESSLIST_COMMAND VARCHAR(16),"
46 "PROCESSLIST_TIME BIGINT,"
47 "PROCESSLIST_STATE VARCHAR(64),"
48 "PROCESSLIST_INFO LONGTEXT,"
49 "PARENT_THREAD_ID BIGINT unsigned,"
50 "ROLE VARCHAR(64),"
51 "INSTRUMENTED ENUM ('YES', 'NO') not null)") }
52};
53
54PFS_engine_table* table_threads::create()
55{
56 return new table_threads();
57}
58
59table_threads::table_threads()
60 : cursor_by_thread(& m_share),
61 m_row_exists(false)
62{}
63
64void table_threads::make_row(PFS_thread *pfs)
65{
66 pfs_lock lock;
67 pfs_lock session_lock;
68 pfs_lock stmt_lock;
69 PFS_stage_class *stage_class;
70 PFS_thread_class *safe_class;
71
72 m_row_exists= false;
73
74 /* Protect this reader against thread termination */
75 pfs->m_lock.begin_optimistic_lock(&lock);
76
77 safe_class= sanitize_thread_class(pfs->m_class);
78 if (unlikely(safe_class == NULL))
79 return;
80
81 m_row.m_thread_internal_id= pfs->m_thread_internal_id;
82 m_row.m_parent_thread_internal_id= pfs->m_parent_thread_internal_id;
83 m_row.m_processlist_id= pfs->m_processlist_id;
84 m_row.m_name= safe_class->m_name;
85 m_row.m_name_length= safe_class->m_name_length;
86
87 /* Protect this reader against session attribute changes */
88 pfs->m_session_lock.begin_optimistic_lock(&session_lock);
89
90 m_row.m_username_length= pfs->m_username_length;
91 if (unlikely(m_row.m_username_length > sizeof(m_row.m_username)))
92 return;
93 if (m_row.m_username_length != 0)
94 memcpy(m_row.m_username, pfs->m_username, m_row.m_username_length);
95
96 m_row.m_hostname_length= pfs->m_hostname_length;
97 if (unlikely(m_row.m_hostname_length > sizeof(m_row.m_hostname)))
98 return;
99 if (m_row.m_hostname_length != 0)
100 memcpy(m_row.m_hostname, pfs->m_hostname, m_row.m_hostname_length);
101
102 if (! pfs->m_session_lock.end_optimistic_lock(& session_lock))
103 {
104 /*
105 One of the columns:
106 - PROCESSLIST_USER
107 - PROCESSLIST_HOST
108 is being updated.
109 Do not discard the entire row.
110 Do not loop waiting for a stable value.
111 Just return NULL values.
112 */
113 m_row.m_username_length= 0;
114 m_row.m_hostname_length= 0;
115 }
116
117 /* Protect this reader against statement attributes changes */
118 pfs->m_stmt_lock.begin_optimistic_lock(&stmt_lock);
119
120 m_row.m_dbname_length= pfs->m_dbname_length;
121 if (unlikely(m_row.m_dbname_length > sizeof(m_row.m_dbname)))
122 return;
123 if (m_row.m_dbname_length != 0)
124 memcpy(m_row.m_dbname, pfs->m_dbname, m_row.m_dbname_length);
125
126 m_row.m_processlist_info_ptr= & pfs->m_processlist_info[0];
127 m_row.m_processlist_info_length= pfs->m_processlist_info_length;
128
129 if (! pfs->m_stmt_lock.end_optimistic_lock(& stmt_lock))
130 {
131 /*
132 One of the columns:
133 - PROCESSLIST_DB
134 - PROCESSLIST_INFO
135 is being updated.
136 Do not discard the entire row.
137 Do not loop waiting for a stable value.
138 Just return NULL values.
139 */
140 m_row.m_dbname_length= 0;
141 m_row.m_processlist_info_length= 0;
142 }
143
144 /* Dirty read, sanitize the command. */
145 m_row.m_command= pfs->m_command;
146 if ((m_row.m_command < 0) || (m_row.m_command > COM_END))
147 m_row.m_command= COM_END;
148
149 m_row.m_start_time= pfs->m_start_time;
150
151 stage_class= find_stage_class(pfs->m_stage);
152 if (stage_class != NULL)
153 {
154 m_row.m_processlist_state_ptr= stage_class->m_name + stage_class->m_prefix_length;
155 m_row.m_processlist_state_length= stage_class->m_name_length - stage_class->m_prefix_length;
156 }
157 else
158 {
159 m_row.m_processlist_state_length= 0;
160 }
161
162 m_row.m_enabled_ptr= &pfs->m_enabled;
163
164 if (pfs->m_lock.end_optimistic_lock(& lock))
165 m_row_exists= true;
166}
167
168int table_threads::read_row_values(TABLE *table,
169 unsigned char *buf,
170 Field **fields,
171 bool read_all)
172{
173 Field *f;
174
175 if (unlikely(! m_row_exists))
176 return HA_ERR_RECORD_DELETED;
177
178 /* Set the null bits */
179 DBUG_ASSERT(table->s->null_bytes == 2);
180 buf[0]= 0;
181 buf[1]= 0;
182
183 for (; (f= *fields) ; fields++)
184 {
185 if (read_all || bitmap_is_set(table->read_set, f->field_index))
186 {
187 switch(f->field_index)
188 {
189 case 0: /* THREAD_ID */
190 set_field_ulonglong(f, m_row.m_thread_internal_id);
191 break;
192 case 1: /* NAME */
193 set_field_varchar_utf8(f, m_row.m_name, m_row.m_name_length);
194 break;
195 case 2: /* TYPE */
196 if (m_row.m_processlist_id != 0)
197 set_field_varchar_utf8(f, "FOREGROUND", 10);
198 else
199 set_field_varchar_utf8(f, "BACKGROUND", 10);
200 break;
201 case 3: /* PROCESSLIST_ID */
202 if (m_row.m_processlist_id != 0)
203 set_field_ulonglong(f, m_row.m_processlist_id);
204 else
205 f->set_null();
206 break;
207 case 4: /* PROCESSLIST_USER */
208 if (m_row.m_username_length > 0)
209 set_field_varchar_utf8(f, m_row.m_username,
210 m_row.m_username_length);
211 else
212 f->set_null();
213 break;
214 case 5: /* PROCESSLIST_HOST */
215 if (m_row.m_hostname_length > 0)
216 set_field_varchar_utf8(f, m_row.m_hostname,
217 m_row.m_hostname_length);
218 else
219 f->set_null();
220 break;
221 case 6: /* PROCESSLIST_DB */
222 if (m_row.m_dbname_length > 0)
223 set_field_varchar_utf8(f, m_row.m_dbname,
224 m_row.m_dbname_length);
225 else
226 f->set_null();
227 break;
228 case 7: /* PROCESSLIST_COMMAND */
229 if (m_row.m_processlist_id != 0)
230 set_field_varchar_utf8(f, command_name[m_row.m_command].str,
231 (uint)command_name[m_row.m_command].length);
232 else
233 f->set_null();
234 break;
235 case 8: /* PROCESSLIST_TIME */
236 if (m_row.m_start_time)
237 {
238 time_t now= my_time(0);
239 ulonglong elapsed= (now > m_row.m_start_time ? now - m_row.m_start_time : 0);
240 set_field_ulonglong(f, elapsed);
241 }
242 else
243 f->set_null();
244 break;
245 case 9: /* PROCESSLIST_STATE */
246 if (m_row.m_processlist_state_length > 0)
247 {
248 /* This column's datatype is declared as varchar(64). But in current
249 code, there are few process state messages which are greater than
250 64 characters(Eg:stage_slave_has_read_all_relay_log).
251 In those cases, we will end up in 'data truncated'
252 warning/error (depends sql_mode setting) when server is updating
253 this column for those threads. Since 5.6 is GAed, neither the
254 metadata of this column can be changed, nor those state messages.
255 So server will silently truncate the state message to 64 characters
256 if it is longer. In Upper versions(5.7+), these state messages are
257 changed to less than or equal to 64 characters.
258 */
259 set_field_varchar_utf8(f, m_row.m_processlist_state_ptr,
260 MY_MIN(m_row.m_processlist_state_length,
261 f->char_length()));
262 }
263 else
264 f->set_null();
265 break;
266 case 10: /* PROCESSLIST_INFO */
267 if (m_row.m_processlist_info_length > 0)
268 set_field_longtext_utf8(f, m_row.m_processlist_info_ptr,
269 m_row.m_processlist_info_length);
270 else
271 f->set_null();
272 break;
273 case 11: /* PARENT_THREAD_ID */
274 if (m_row.m_parent_thread_internal_id != 0)
275 set_field_ulonglong(f, m_row.m_parent_thread_internal_id);
276 else
277 f->set_null();
278 break;
279 case 12: /* ROLE */
280 f->set_null();
281 break;
282 case 13: /* INSTRUMENTED */
283 set_field_enum(f, (*m_row.m_enabled_ptr) ? ENUM_YES : ENUM_NO);
284 break;
285 default:
286 DBUG_ASSERT(false);
287 }
288 }
289 }
290 return 0;
291}
292
293int table_threads::update_row_values(TABLE *table,
294 const unsigned char *old_buf,
295 const unsigned char *new_buf,
296 Field **fields)
297{
298 Field *f;
299 enum_yes_no value;
300
301 for (; (f= *fields) ; fields++)
302 {
303 if (bitmap_is_set(table->write_set, f->field_index))
304 {
305 switch(f->field_index)
306 {
307 case 0: /* THREAD_ID */
308 case 1: /* NAME */
309 case 2: /* TYPE */
310 case 3: /* PROCESSLIST_ID */
311 case 4: /* PROCESSLIST_USER */
312 case 5: /* PROCESSLIST_HOST */
313 case 6: /* PROCESSLIST_DB */
314 case 7: /* PROCESSLIST_COMMAND */
315 case 8: /* PROCESSLIST_TIME */
316 case 9: /* PROCESSLIST_STATE */
317 case 10: /* PROCESSLIST_INFO */
318 case 11: /* PARENT_THREAD_ID */
319 case 12: /* ROLE */
320 return HA_ERR_WRONG_COMMAND;
321 case 13: /* INSTRUMENTED */
322 value= (enum_yes_no) get_field_enum(f);
323 *m_row.m_enabled_ptr= (value == ENUM_YES) ? true : false;
324 break;
325 default:
326 DBUG_ASSERT(false);
327 }
328 }
329 }
330 return 0;
331}
332
333