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 | /** |
17 | @file storage/perfschema/ha_perfschema.cc |
18 | Performance schema storage engine (implementation). |
19 | */ |
20 | |
21 | #include "sql_plugin.h" |
22 | #include "my_pthread.h" |
23 | #include "my_atomic.h" |
24 | #include "ha_perfschema.h" |
25 | #include "pfs_engine_table.h" |
26 | #include "pfs_column_values.h" |
27 | #include "pfs_instr_class.h" |
28 | #include "pfs_instr.h" |
29 | #include "pfs_account.h" |
30 | #include "pfs_host.h" |
31 | #include "pfs_user.h" |
32 | #include "pfs_account.h" |
33 | |
34 | handlerton *pfs_hton= NULL; |
35 | |
36 | static handler* pfs_create_handler(handlerton *hton, |
37 | TABLE_SHARE *table, |
38 | MEM_ROOT *mem_root) |
39 | { |
40 | return new (mem_root) ha_perfschema(hton, table); |
41 | } |
42 | |
43 | static int compare_database_names(const char *name1, const char *name2) |
44 | { |
45 | if (lower_case_table_names) |
46 | return strcasecmp(name1, name2); |
47 | return strcmp(name1, name2); |
48 | } |
49 | |
50 | static const PFS_engine_table_share* |
51 | find_table_share(const char *db, const char *name) |
52 | { |
53 | DBUG_ENTER("find_table_share" ); |
54 | |
55 | if (compare_database_names(db, PERFORMANCE_SCHEMA_str.str) != 0) |
56 | DBUG_RETURN(NULL); |
57 | |
58 | const PFS_engine_table_share* result; |
59 | result= PFS_engine_table::find_engine_table_share(name); |
60 | DBUG_RETURN(result); |
61 | } |
62 | |
63 | static int pfs_discover_table(handlerton *hton, THD *thd, TABLE_SHARE *share) |
64 | { |
65 | const PFS_engine_table_share *pfs_share; |
66 | |
67 | if ((pfs_share= find_table_share(share->db.str, share->table_name.str))) |
68 | return share->init_from_sql_statement_string(thd, false, |
69 | pfs_share->sql.str, |
70 | pfs_share->sql.length); |
71 | return HA_ERR_NO_SUCH_TABLE; |
72 | } |
73 | |
74 | static int pfs_discover_table_existence(handlerton *hton, const char *db, |
75 | const char *table_name) |
76 | { |
77 | return MY_TEST(find_table_share(db, table_name)); |
78 | } |
79 | |
80 | static int pfs_init_func(void *p) |
81 | { |
82 | DBUG_ENTER("pfs_init_func" ); |
83 | |
84 | pfs_hton= reinterpret_cast<handlerton *> (p); |
85 | |
86 | pfs_hton->state= SHOW_OPTION_YES; |
87 | pfs_hton->create= pfs_create_handler; |
88 | pfs_hton->show_status= pfs_show_status; |
89 | pfs_hton->flags= HTON_ALTER_NOT_SUPPORTED | |
90 | HTON_TEMPORARY_NOT_SUPPORTED | |
91 | HTON_NO_PARTITION | |
92 | HTON_NO_BINLOG_ROW_OPT; |
93 | |
94 | /* |
95 | As long as the server implementation keeps using legacy_db_type, |
96 | as for example in mysql_truncate(), |
97 | we can not rely on the fact that different mysqld process will assign |
98 | consistently the same legacy_db_type for a given storage engine name. |
99 | In particular, using different --loose-skip-xxx options between |
100 | ./mysqld --bootstrap |
101 | ./mysqld |
102 | creates bogus .frm forms when bootstrapping the performance schema, |
103 | if we rely on ha_initialize_handlerton to assign a really dynamic value. |
104 | To fix this, a dedicated DB_TYPE is officially assigned to |
105 | the performance schema. See Bug#43039. |
106 | */ |
107 | pfs_hton->db_type= DB_TYPE_PERFORMANCE_SCHEMA; |
108 | pfs_hton->discover_table= pfs_discover_table; |
109 | pfs_hton->discover_table_existence= pfs_discover_table_existence; |
110 | pfs_hton->discover_table_names= pfs_discover_table_names; |
111 | |
112 | PFS_engine_table_share::init_all_locks(); |
113 | |
114 | DBUG_RETURN(0); |
115 | } |
116 | |
117 | static int pfs_done_func(void *p) |
118 | { |
119 | DBUG_ENTER("pfs_done_func" ); |
120 | |
121 | pfs_hton= NULL; |
122 | |
123 | PFS_engine_table_share::delete_all_locks(); |
124 | |
125 | DBUG_RETURN(0); |
126 | } |
127 | |
128 | static struct st_mysql_show_var pfs_status_vars[]= |
129 | { |
130 | {"Performance_schema_mutex_classes_lost" , |
131 | (char*) &mutex_class_lost, SHOW_LONG_NOFLUSH}, |
132 | {"Performance_schema_rwlock_classes_lost" , |
133 | (char*) &rwlock_class_lost, SHOW_LONG_NOFLUSH}, |
134 | {"Performance_schema_cond_classes_lost" , |
135 | (char*) &cond_class_lost, SHOW_LONG_NOFLUSH}, |
136 | {"Performance_schema_thread_classes_lost" , |
137 | (char*) &thread_class_lost, SHOW_LONG_NOFLUSH}, |
138 | {"Performance_schema_file_classes_lost" , |
139 | (char*) &file_class_lost, SHOW_LONG_NOFLUSH}, |
140 | {"Performance_schema_socket_classes_lost" , |
141 | (char*) &socket_class_lost, SHOW_LONG_NOFLUSH}, |
142 | {"Performance_schema_mutex_instances_lost" , |
143 | (char*) &mutex_lost, SHOW_LONG}, |
144 | {"Performance_schema_rwlock_instances_lost" , |
145 | (char*) &rwlock_lost, SHOW_LONG}, |
146 | {"Performance_schema_cond_instances_lost" , |
147 | (char*) &cond_lost, SHOW_LONG}, |
148 | {"Performance_schema_thread_instances_lost" , |
149 | (char*) &thread_lost, SHOW_LONG}, |
150 | {"Performance_schema_file_instances_lost" , |
151 | (char*) &file_lost, SHOW_LONG}, |
152 | {"Performance_schema_file_handles_lost" , |
153 | (char*) &file_handle_lost, SHOW_LONG}, |
154 | {"Performance_schema_socket_instances_lost" , |
155 | (char*) &socket_lost, SHOW_LONG}, |
156 | {"Performance_schema_locker_lost" , |
157 | (char*) &locker_lost, SHOW_LONG}, |
158 | /* table shares, can be flushed */ |
159 | {"Performance_schema_table_instances_lost" , |
160 | (char*) &table_share_lost, SHOW_LONG}, |
161 | /* table handles, can be flushed */ |
162 | {"Performance_schema_table_handles_lost" , |
163 | (char*) &table_lost, SHOW_LONG}, |
164 | {"Performance_schema_hosts_lost" , |
165 | (char*) &host_lost, SHOW_LONG}, |
166 | {"Performance_schema_users_lost" , |
167 | (char*) &user_lost, SHOW_LONG}, |
168 | {"Performance_schema_accounts_lost" , |
169 | (char*) &account_lost, SHOW_LONG}, |
170 | {"Performance_schema_stage_classes_lost" , |
171 | (char*) &stage_class_lost, SHOW_LONG}, |
172 | {"Performance_schema_statement_classes_lost" , |
173 | (char*) &statement_class_lost, SHOW_LONG}, |
174 | {"Performance_schema_digest_lost" , |
175 | (char*) &digest_lost, SHOW_LONG}, |
176 | {"Performance_schema_session_connect_attrs_lost" , |
177 | (char*) &session_connect_attrs_lost, SHOW_LONG}, |
178 | {NullS, NullS, SHOW_LONG} |
179 | }; |
180 | |
181 | struct st_mysql_storage_engine pfs_storage_engine= |
182 | { MYSQL_HANDLERTON_INTERFACE_VERSION }; |
183 | |
184 | const char* pfs_engine_name= "PERFORMANCE_SCHEMA" ; |
185 | |
186 | mysql_declare_plugin(perfschema) |
187 | { |
188 | MYSQL_STORAGE_ENGINE_PLUGIN, |
189 | &pfs_storage_engine, |
190 | pfs_engine_name, |
191 | "Marc Alff, Oracle" , /* Formerly Sun Microsystems, formerly MySQL */ |
192 | "Performance Schema" , |
193 | PLUGIN_LICENSE_GPL, |
194 | pfs_init_func, /* Plugin Init */ |
195 | pfs_done_func, /* Plugin Deinit */ |
196 | 0x0001 /* 0.1 */, |
197 | pfs_status_vars, /* status variables */ |
198 | NULL, /* system variables */ |
199 | NULL, /* config options */ |
200 | 0, /* flags */ |
201 | } |
202 | mysql_declare_plugin_end; |
203 | |
204 | maria_declare_plugin(perfschema) |
205 | { |
206 | MYSQL_STORAGE_ENGINE_PLUGIN, |
207 | &pfs_storage_engine, |
208 | pfs_engine_name, |
209 | "Marc Alff, Oracle" , |
210 | "Performance Schema" , |
211 | PLUGIN_LICENSE_GPL, |
212 | pfs_init_func, |
213 | pfs_done_func, |
214 | 0x0001, |
215 | pfs_status_vars, |
216 | NULL, |
217 | "5.6.40" , |
218 | MariaDB_PLUGIN_MATURITY_STABLE |
219 | } |
220 | maria_declare_plugin_end; |
221 | |
222 | ha_perfschema::ha_perfschema(handlerton *hton, TABLE_SHARE *share) |
223 | : handler(hton, share), m_table_share(NULL), m_table(NULL) |
224 | {} |
225 | |
226 | ha_perfschema::~ha_perfschema() |
227 | {} |
228 | |
229 | int ha_perfschema::open(const char *name, int mode, uint test_if_locked) |
230 | { |
231 | DBUG_ENTER("ha_perfschema::open" ); |
232 | |
233 | m_table_share= find_table_share(table_share->db.str, |
234 | table_share->table_name.str); |
235 | if (! m_table_share) |
236 | DBUG_RETURN(HA_ERR_NO_SUCH_TABLE); |
237 | |
238 | thr_lock_data_init(m_table_share->m_thr_lock_ptr, &m_thr_lock, NULL); |
239 | ref_length= m_table_share->m_ref_length; |
240 | |
241 | DBUG_RETURN(0); |
242 | } |
243 | |
244 | int ha_perfschema::close(void) |
245 | { |
246 | DBUG_ENTER("ha_perfschema::close" ); |
247 | m_table_share= NULL; |
248 | delete m_table; |
249 | m_table= NULL; |
250 | |
251 | DBUG_RETURN(0); |
252 | } |
253 | |
254 | int ha_perfschema::write_row(uchar *buf) |
255 | { |
256 | int result; |
257 | |
258 | DBUG_ENTER("ha_perfschema::write_row" ); |
259 | if (!pfs_initialized) |
260 | DBUG_RETURN(HA_ERR_WRONG_COMMAND); |
261 | |
262 | DBUG_ASSERT(m_table_share); |
263 | result= m_table_share->write_row(table, buf, table->field); |
264 | DBUG_RETURN(result); |
265 | } |
266 | |
267 | void ha_perfschema::use_hidden_primary_key(void) |
268 | { |
269 | /* |
270 | This is also called in case of row based replication, |
271 | see TABLE::mark_columns_needed_for_update(). |
272 | Add all columns to the read set, but do not touch the write set, |
273 | as some columns in the SETUP_ tables are not writable. |
274 | */ |
275 | table->column_bitmaps_set_no_signal(&table->s->all_set, table->write_set); |
276 | } |
277 | |
278 | int ha_perfschema::update_row(const uchar *old_data, const uchar *new_data) |
279 | { |
280 | DBUG_ENTER("ha_perfschema::update_row" ); |
281 | if (!pfs_initialized) |
282 | DBUG_RETURN(HA_ERR_WRONG_COMMAND); |
283 | |
284 | if (is_executed_by_slave()) |
285 | DBUG_RETURN(0); |
286 | |
287 | DBUG_ASSERT(m_table); |
288 | int result= m_table->update_row(table, old_data, new_data, table->field); |
289 | DBUG_RETURN(result); |
290 | } |
291 | |
292 | int ha_perfschema::delete_row(const uchar *buf) |
293 | { |
294 | DBUG_ENTER("ha_perfschema::delete_row" ); |
295 | if (!pfs_initialized) |
296 | DBUG_RETURN(HA_ERR_WRONG_COMMAND); |
297 | |
298 | DBUG_ASSERT(m_table); |
299 | int result= m_table->delete_row(table, buf, table->field); |
300 | DBUG_RETURN(result); |
301 | } |
302 | |
303 | int ha_perfschema::rnd_init(bool scan) |
304 | { |
305 | int result; |
306 | DBUG_ENTER("ha_perfschema::rnd_init" ); |
307 | |
308 | DBUG_ASSERT(m_table_share); |
309 | DBUG_ASSERT(m_table_share->m_open_table != NULL); |
310 | |
311 | stats.records= 0; |
312 | if (m_table == NULL) |
313 | m_table= m_table_share->m_open_table(); |
314 | else |
315 | m_table->reset_position(); |
316 | |
317 | if (m_table != NULL) |
318 | m_table->rnd_init(scan); |
319 | |
320 | result= m_table ? 0 : HA_ERR_OUT_OF_MEM; |
321 | DBUG_RETURN(result); |
322 | } |
323 | |
324 | int ha_perfschema::rnd_end(void) |
325 | { |
326 | DBUG_ENTER("ha_perfschema::rnd_end" ); |
327 | DBUG_ASSERT(m_table); |
328 | delete m_table; |
329 | m_table= NULL; |
330 | DBUG_RETURN(0); |
331 | } |
332 | |
333 | int ha_perfschema::rnd_next(uchar *buf) |
334 | { |
335 | DBUG_ENTER("ha_perfschema::rnd_next" ); |
336 | if (!pfs_initialized) |
337 | { |
338 | table->status= STATUS_NOT_FOUND; |
339 | DBUG_RETURN(HA_ERR_END_OF_FILE); |
340 | } |
341 | |
342 | DBUG_ASSERT(m_table); |
343 | |
344 | int result= m_table->rnd_next(); |
345 | if (result == 0) |
346 | { |
347 | result= m_table->read_row(table, buf, table->field); |
348 | if (result == 0) |
349 | stats.records++; |
350 | } |
351 | table->status= (result ? STATUS_NOT_FOUND : 0); |
352 | DBUG_RETURN(result); |
353 | } |
354 | |
355 | void ha_perfschema::position(const uchar *record) |
356 | { |
357 | DBUG_ENTER("ha_perfschema::position" ); |
358 | |
359 | DBUG_ASSERT(m_table); |
360 | m_table->get_position(ref); |
361 | DBUG_VOID_RETURN; |
362 | } |
363 | |
364 | int ha_perfschema::rnd_pos(uchar *buf, uchar *pos) |
365 | { |
366 | DBUG_ENTER("ha_perfschema::rnd_pos" ); |
367 | if (!pfs_initialized) |
368 | { |
369 | table->status= STATUS_NOT_FOUND; |
370 | DBUG_RETURN(HA_ERR_END_OF_FILE); |
371 | } |
372 | |
373 | DBUG_ASSERT(m_table); |
374 | int result= m_table->rnd_pos(pos); |
375 | if (result == 0) |
376 | result= m_table->read_row(table, buf, table->field); |
377 | table->status= (result ? STATUS_NOT_FOUND : 0); |
378 | DBUG_RETURN(result); |
379 | } |
380 | |
381 | int ha_perfschema::info(uint flag) |
382 | { |
383 | DBUG_ENTER("ha_perfschema::info" ); |
384 | DBUG_ASSERT(m_table_share); |
385 | if (flag & HA_STATUS_VARIABLE) |
386 | stats.records= m_table_share->get_row_count(); |
387 | if (flag & HA_STATUS_CONST) |
388 | ref_length= m_table_share->m_ref_length; |
389 | DBUG_RETURN(0); |
390 | } |
391 | |
392 | int ha_perfschema::delete_all_rows(void) |
393 | { |
394 | int result; |
395 | |
396 | DBUG_ENTER("ha_perfschema::delete_all_rows" ); |
397 | if (!pfs_initialized) |
398 | DBUG_RETURN(0); |
399 | |
400 | if (is_executed_by_slave()) |
401 | DBUG_RETURN(0); |
402 | |
403 | DBUG_ASSERT(m_table_share); |
404 | if (m_table_share->m_delete_all_rows) |
405 | result= m_table_share->m_delete_all_rows(); |
406 | else |
407 | { |
408 | result= HA_ERR_WRONG_COMMAND; |
409 | } |
410 | DBUG_RETURN(result); |
411 | } |
412 | |
413 | int ha_perfschema::truncate() |
414 | { |
415 | return delete_all_rows(); |
416 | } |
417 | |
418 | THR_LOCK_DATA **ha_perfschema::store_lock(THD *thd, |
419 | THR_LOCK_DATA **to, |
420 | enum thr_lock_type lock_type) |
421 | { |
422 | if (lock_type != TL_IGNORE && m_thr_lock.type == TL_UNLOCK) |
423 | m_thr_lock.type= lock_type; |
424 | *to++= &m_thr_lock; |
425 | m_thr_lock.m_psi= m_psi; |
426 | return to; |
427 | } |
428 | |
429 | int ha_perfschema::delete_table(const char *name) |
430 | { |
431 | DBUG_ENTER("ha_perfschema::delete_table" ); |
432 | DBUG_RETURN(0); |
433 | } |
434 | |
435 | int ha_perfschema::rename_table(const char * from, const char * to) |
436 | { |
437 | DBUG_ENTER("ha_perfschema::rename_table " ); |
438 | DBUG_RETURN(HA_ERR_WRONG_COMMAND); |
439 | } |
440 | |
441 | int ha_perfschema::create(const char *name, TABLE *table_arg, |
442 | HA_CREATE_INFO *create_info) |
443 | { |
444 | DBUG_ENTER("ha_perfschema::create" ); |
445 | /* |
446 | This is not a general purpose engine. |
447 | Failure to CREATE TABLE is the expected result. |
448 | */ |
449 | DBUG_RETURN(HA_ERR_WRONG_COMMAND); |
450 | } |
451 | |
452 | void ha_perfschema::print_error(int error, myf errflag) |
453 | { |
454 | switch (error) |
455 | { |
456 | case HA_ERR_TABLE_NEEDS_UPGRADE: |
457 | /* |
458 | The error message for ER_TABLE_NEEDS_UPGRADE refers to REPAIR table, |
459 | which does not apply to performance schema tables. |
460 | */ |
461 | my_error(ER_WRONG_NATIVE_TABLE_STRUCTURE, MYF(0), |
462 | table_share->db.str, table_share->table_name.str); |
463 | break; |
464 | case HA_ERR_WRONG_COMMAND: |
465 | /* |
466 | The performance schema is not a general purpose storage engine, |
467 | some operations are not supported, by design. |
468 | We do not want to print "Command not supported", |
469 | which gives the impression that a command implementation is missing, |
470 | and that the failure should be considered a bug. |
471 | We print "Invalid performance_schema usage." instead, |
472 | to emphasise that the operation attempted is not meant to be legal, |
473 | and that the failure returned is indeed the expected result. |
474 | */ |
475 | my_error(ER_WRONG_PERFSCHEMA_USAGE, MYF(0)); |
476 | break; |
477 | default: |
478 | handler::print_error(error, errflag); |
479 | break; |
480 | } |
481 | } |
482 | |
483 | |