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, 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_pthread.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_account.h"
42 
43 #ifdef MY_ATOMIC_MODE_DUMMY
44 /*
45   The performance schema can can not function with MY_ATOMIC_MODE_DUMMY,
46   a fully functional implementation of MY_ATOMIC should be used instead.
47   If the build fails with this error message:
48   - either use a different ./configure --with-atomic-ops option
49   - or do not build with the performance schema.
50 */
51 #error "The performance schema needs a functional MY_ATOMIC implementation."
52 #endif
53 
54 handlerton *pfs_hton= NULL;
55 
pfs_create_handler(handlerton * hton,TABLE_SHARE * table,MEM_ROOT * mem_root)56 static handler* pfs_create_handler(handlerton *hton,
57                                    TABLE_SHARE *table,
58                                    MEM_ROOT *mem_root)
59 {
60   return new (mem_root) ha_perfschema(hton, table);
61 }
62 
compare_database_names(const char * name1,const char * name2)63 static int compare_database_names(const char *name1, const char *name2)
64 {
65   if (lower_case_table_names)
66     return strcasecmp(name1, name2);
67   return strcmp(name1, name2);
68 }
69 
70 static const PFS_engine_table_share*
find_table_share(const char * db,const char * name)71 find_table_share(const char *db, const char *name)
72 {
73   DBUG_ENTER("find_table_share");
74 
75   if (compare_database_names(db, PERFORMANCE_SCHEMA_str.str) != 0)
76     DBUG_RETURN(NULL);
77 
78   const PFS_engine_table_share* result;
79   result= PFS_engine_table::find_engine_table_share(name);
80   DBUG_RETURN(result);
81 }
82 
pfs_init_func(void * p)83 static int pfs_init_func(void *p)
84 {
85   DBUG_ENTER("pfs_init_func");
86 
87   pfs_hton= reinterpret_cast<handlerton *> (p);
88 
89   pfs_hton->state= SHOW_OPTION_YES;
90   pfs_hton->create= pfs_create_handler;
91   pfs_hton->show_status= pfs_show_status;
92   pfs_hton->flags= HTON_ALTER_NOT_SUPPORTED |
93     HTON_TEMPORARY_NOT_SUPPORTED |
94     HTON_NO_PARTITION |
95     HTON_NO_BINLOG_ROW_OPT;
96 
97   /*
98     As long as the server implementation keeps using legacy_db_type,
99     as for example in mysql_truncate(),
100     we can not rely on the fact that different mysqld process will assign
101     consistently the same legacy_db_type for a given storage engine name.
102     In particular, using different --loose-skip-xxx options between
103     ./mysqld --bootstrap
104     ./mysqld
105     creates bogus .frm forms when bootstrapping the performance schema,
106     if we rely on ha_initialize_handlerton to assign a really dynamic value.
107     To fix this, a dedicated DB_TYPE is officially assigned to
108     the performance schema. See Bug#43039.
109   */
110   pfs_hton->db_type= DB_TYPE_PERFORMANCE_SCHEMA;
111 
112   PFS_engine_table_share::init_all_locks();
113 
114   DBUG_RETURN(0);
115 }
116 
pfs_done_func(void * p)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 
mysql_declare_plugin(perfschema)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 
ha_perfschema(handlerton * hton,TABLE_SHARE * share)204 ha_perfschema::ha_perfschema(handlerton *hton, TABLE_SHARE *share)
205   : handler(hton, share), m_table_share(NULL), m_table(NULL)
206 {}
207 
~ha_perfschema()208 ha_perfschema::~ha_perfschema()
209 {}
210 
211 static const char *ha_pfs_exts[]= {
212   NullS
213 };
214 
bas_ext() const215 const char **ha_perfschema::bas_ext() const
216 {
217   return ha_pfs_exts;
218 }
219 
open(const char * name,int mode,uint test_if_locked)220 int ha_perfschema::open(const char *name, int mode, uint test_if_locked)
221 {
222   DBUG_ENTER("ha_perfschema::open");
223 
224   m_table_share= find_table_share(table_share->db.str,
225                                   table_share->table_name.str);
226   if (! m_table_share)
227     DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
228 
229   thr_lock_data_init(m_table_share->m_thr_lock_ptr, &m_thr_lock, NULL);
230   ref_length= m_table_share->m_ref_length;
231 
232   DBUG_RETURN(0);
233 }
234 
close(void)235 int ha_perfschema::close(void)
236 {
237   DBUG_ENTER("ha_perfschema::close");
238   m_table_share= NULL;
239   delete m_table;
240   m_table= NULL;
241 
242   DBUG_RETURN(0);
243 }
244 
write_row(uchar * buf)245 int ha_perfschema::write_row(uchar *buf)
246 {
247   int result;
248 
249   DBUG_ENTER("ha_perfschema::write_row");
250   if (!pfs_initialized)
251     DBUG_RETURN(HA_ERR_WRONG_COMMAND);
252 
253   DBUG_ASSERT(m_table_share);
254   ha_statistic_increment(&SSV::ha_write_count);
255   result= m_table_share->write_row(table, buf, table->field);
256   DBUG_RETURN(result);
257 }
258 
use_hidden_primary_key(void)259 void ha_perfschema::use_hidden_primary_key(void)
260 {
261   /*
262     This is also called in case of row based replication,
263     see TABLE::mark_columns_needed_for_update().
264     Add all columns to the read set, but do not touch the write set,
265     as some columns in the SETUP_ tables are not writable.
266   */
267   table->column_bitmaps_set_no_signal(&table->s->all_set, table->write_set);
268 }
269 
update_row(const uchar * old_data,uchar * new_data)270 int ha_perfschema::update_row(const uchar *old_data, uchar *new_data)
271 {
272   DBUG_ENTER("ha_perfschema::update_row");
273   if (!pfs_initialized)
274     DBUG_RETURN(HA_ERR_WRONG_COMMAND);
275 
276   if (is_executed_by_slave())
277     DBUG_RETURN(0);
278 
279   DBUG_ASSERT(m_table);
280   ha_statistic_increment(&SSV::ha_update_count);
281   int result= m_table->update_row(table, old_data, new_data, table->field);
282   DBUG_RETURN(result);
283 }
284 
delete_row(const uchar * buf)285 int ha_perfschema::delete_row(const uchar *buf)
286 {
287   DBUG_ENTER("ha_perfschema::delete_row");
288   if (!pfs_initialized)
289     DBUG_RETURN(HA_ERR_WRONG_COMMAND);
290 
291   DBUG_ASSERT(m_table);
292   ha_statistic_increment(&SSV::ha_delete_count);
293   int result= m_table->delete_row(table, buf, table->field);
294   DBUG_RETURN(result);
295 }
296 
rnd_init(bool scan)297 int ha_perfschema::rnd_init(bool scan)
298 {
299   int result;
300   DBUG_ENTER("ha_perfschema::rnd_init");
301 
302   DBUG_ASSERT(m_table_share);
303   DBUG_ASSERT(m_table_share->m_open_table != NULL);
304 
305   stats.records= 0;
306   if (m_table == NULL)
307     m_table= m_table_share->m_open_table();
308   else
309     m_table->reset_position();
310 
311   if (m_table != NULL)
312     m_table->rnd_init(scan);
313 
314   result= m_table ? 0 : HA_ERR_OUT_OF_MEM;
315   DBUG_RETURN(result);
316 }
317 
rnd_end(void)318 int ha_perfschema::rnd_end(void)
319 {
320   DBUG_ENTER("ha_perfschema::rnd_end");
321   DBUG_ASSERT(m_table);
322   delete m_table;
323   m_table= NULL;
324   DBUG_RETURN(0);
325 }
326 
rnd_next(uchar * buf)327 int ha_perfschema::rnd_next(uchar *buf)
328 {
329   DBUG_ENTER("ha_perfschema::rnd_next");
330   if (!pfs_initialized)
331   {
332     table->status= STATUS_NOT_FOUND;
333     DBUG_RETURN(HA_ERR_END_OF_FILE);
334   }
335 
336   DBUG_ASSERT(m_table);
337   ha_statistic_increment(&SSV::ha_read_rnd_next_count);
338 
339   int result= m_table->rnd_next();
340   if (result == 0)
341   {
342     result= m_table->read_row(table, buf, table->field);
343     if (result == 0)
344       stats.records++;
345   }
346   table->status= (result ? STATUS_NOT_FOUND : 0);
347   DBUG_RETURN(result);
348 }
349 
position(const uchar * record)350 void ha_perfschema::position(const uchar *record)
351 {
352   DBUG_ENTER("ha_perfschema::position");
353 
354   DBUG_ASSERT(m_table);
355   m_table->get_position(ref);
356   DBUG_VOID_RETURN;
357 }
358 
rnd_pos(uchar * buf,uchar * pos)359 int ha_perfschema::rnd_pos(uchar *buf, uchar *pos)
360 {
361   DBUG_ENTER("ha_perfschema::rnd_pos");
362   if (!pfs_initialized)
363   {
364     table->status= STATUS_NOT_FOUND;
365     DBUG_RETURN(HA_ERR_END_OF_FILE);
366   }
367 
368   DBUG_ASSERT(m_table);
369   ha_statistic_increment(&SSV::ha_read_rnd_count);
370   int result= m_table->rnd_pos(pos);
371   if (result == 0)
372     result= m_table->read_row(table, buf, table->field);
373   table->status= (result ? STATUS_NOT_FOUND : 0);
374   DBUG_RETURN(result);
375 }
376 
info(uint flag)377 int ha_perfschema::info(uint flag)
378 {
379   DBUG_ENTER("ha_perfschema::info");
380   DBUG_ASSERT(m_table_share);
381   if (flag & HA_STATUS_VARIABLE)
382     stats.records= m_table_share->get_row_count();
383   if (flag & HA_STATUS_CONST)
384     ref_length= m_table_share->m_ref_length;
385   DBUG_RETURN(0);
386 }
387 
delete_all_rows(void)388 int ha_perfschema::delete_all_rows(void)
389 {
390   int result;
391 
392   DBUG_ENTER("ha_perfschema::delete_all_rows");
393   if (!pfs_initialized)
394     DBUG_RETURN(0);
395 
396   if (is_executed_by_slave())
397     DBUG_RETURN(0);
398 
399   DBUG_ASSERT(m_table_share);
400   if (m_table_share->m_delete_all_rows)
401     result= m_table_share->m_delete_all_rows();
402   else
403   {
404     result= HA_ERR_WRONG_COMMAND;
405   }
406   DBUG_RETURN(result);
407 }
408 
truncate()409 int ha_perfschema::truncate()
410 {
411   return delete_all_rows();
412 }
413 
store_lock(THD * thd,THR_LOCK_DATA ** to,enum thr_lock_type lock_type)414 THR_LOCK_DATA **ha_perfschema::store_lock(THD *thd,
415                                        THR_LOCK_DATA **to,
416                                        enum thr_lock_type lock_type)
417 {
418   if (lock_type != TL_IGNORE && m_thr_lock.type == TL_UNLOCK)
419     m_thr_lock.type= lock_type;
420   *to++= &m_thr_lock;
421   m_thr_lock.m_psi= m_psi;
422   return to;
423 }
424 
delete_table(const char * name)425 int ha_perfschema::delete_table(const char *name)
426 {
427   DBUG_ENTER("ha_perfschema::delete_table");
428   DBUG_RETURN(0);
429 }
430 
rename_table(const char * from,const char * to)431 int ha_perfschema::rename_table(const char * from, const char * to)
432 {
433   DBUG_ENTER("ha_perfschema::rename_table ");
434   DBUG_RETURN(HA_ERR_WRONG_COMMAND);
435 }
436 
create(const char * name,TABLE * table_arg,HA_CREATE_INFO * create_info)437 int ha_perfschema::create(const char *name, TABLE *table_arg,
438                           HA_CREATE_INFO *create_info)
439 {
440   DBUG_ENTER("ha_perfschema::create");
441   DBUG_ASSERT(table_arg);
442   DBUG_ASSERT(table_arg->s);
443   if (find_table_share(table_arg->s->db.str,
444                        table_arg->s->table_name.str))
445   {
446     /*
447       Attempting to create a known performance schema table.
448       Allowing the create, to create .FRM files,
449       for the initial database install, and mysql_upgrade.
450       This should fail once .FRM are removed.
451     */
452     DBUG_RETURN(0);
453   }
454   /*
455     This is not a general purpose engine.
456     Failure to CREATE TABLE is the expected result.
457   */
458   DBUG_RETURN(HA_ERR_WRONG_COMMAND);
459 }
460 
print_error(int error,myf errflag)461 void ha_perfschema::print_error(int error, myf errflag)
462 {
463   switch (error)
464   {
465     case HA_ERR_TABLE_NEEDS_UPGRADE:
466       /*
467         The error message for ER_TABLE_NEEDS_UPGRADE refers to REPAIR table,
468         which does not apply to performance schema tables.
469       */
470       my_error(ER_WRONG_NATIVE_TABLE_STRUCTURE, MYF(0),
471                table_share->db.str, table_share->table_name.str);
472       break;
473     case HA_ERR_WRONG_COMMAND:
474       /*
475         The performance schema is not a general purpose storage engine,
476         some operations are not supported, by design.
477         We do not want to print "Command not supported",
478         which gives the impression that a command implementation is missing,
479         and that the failure should be considered a bug.
480         We print "Invalid performance_schema usage." instead,
481         to emphasise that the operation attempted is not meant to be legal,
482         and that the failure returned is indeed the expected result.
483       */
484       my_error(ER_WRONG_PERFSCHEMA_USAGE, MYF(0));
485       break;
486     default:
487      handler::print_error(error, errflag);
488      break;
489   }
490 }
491 
492