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