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