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