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