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 | |
23 | THR_LOCK table_threads::m_table_lock; |
24 | |
25 | PFS_engine_table_share |
26 | table_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 | |
54 | PFS_engine_table* table_threads::create() |
55 | { |
56 | return new table_threads(); |
57 | } |
58 | |
59 | table_threads::table_threads() |
60 | : cursor_by_thread(& m_share), |
61 | m_row_exists(false) |
62 | {} |
63 | |
64 | void 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 | |
168 | int 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 | |
293 | int 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 | |