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 as published by
5   the Free Software Foundation; version 2 of the License.
6 
7   This program is distributed in the hope that it will be useful,
8   but WITHOUT ANY WARRANTY; without even the implied warranty of
9   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10   GNU General Public License for more details.
11 
12   You should have received a copy of the GNU General Public License
13   along with this program; if not, write to the Free Software Foundation,
14   51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
15 
16 /**
17   @file storage/perfschema/ha_perfschema.cc
18   Performance schema storage engine (implementation).
19 */
20 
21 #include "my_global.h"
22 #include "my_pthread.h"
23 #include "my_atomic.h"
24 #include "sql_plugin.h"
25 #include "mysql/plugin.h"
26 #include "ha_perfschema.h"
27 #include "pfs_engine_table.h"
28 #include "pfs_column_values.h"
29 #include "pfs_instr_class.h"
30 #include "pfs_instr.h"
31 
32 #ifdef MY_ATOMIC_MODE_DUMMY
33 /*
34   The performance schema can can not function with MY_ATOMIC_MODE_DUMMY,
35   a fully functional implementation of MY_ATOMIC should be used instead.
36   If the build fails with this error message:
37   - either use a different ./configure --with-atomic-ops option
38   - or do not build with the performance schema.
39 */
40 #error "The performance schema needs a functional MY_ATOMIC implementation."
41 #endif
42 
43 handlerton *pfs_hton= NULL;
44 
pfs_create_handler(handlerton * hton,TABLE_SHARE * table,MEM_ROOT * mem_root)45 static handler* pfs_create_handler(handlerton *hton,
46                                    TABLE_SHARE *table,
47                                    MEM_ROOT *mem_root)
48 {
49   return new (mem_root) ha_perfschema(hton, table);
50 }
51 
compare_database_names(const char * name1,const char * name2)52 static int compare_database_names(const char *name1, const char *name2)
53 {
54   if (lower_case_table_names)
55     return strcasecmp(name1, name2);
56   return strcmp(name1, name2);
57 }
58 
59 static const PFS_engine_table_share*
find_table_share(const char * db,const char * name)60 find_table_share(const char *db, const char *name)
61 {
62   DBUG_ENTER("find_table_share");
63 
64   if (compare_database_names(db, PERFORMANCE_SCHEMA_str.str) != 0)
65     DBUG_RETURN(NULL);
66 
67   const PFS_engine_table_share* result;
68   result= PFS_engine_table::find_engine_table_share(name);
69   DBUG_RETURN(result);
70 }
71 
pfs_init_func(void * p)72 static int pfs_init_func(void *p)
73 {
74   DBUG_ENTER("pfs_init_func");
75 
76   pfs_hton= reinterpret_cast<handlerton *> (p);
77 
78   pfs_hton->state= SHOW_OPTION_YES;
79   pfs_hton->create= pfs_create_handler;
80   pfs_hton->show_status= pfs_show_status;
81   pfs_hton->flags= HTON_ALTER_NOT_SUPPORTED |
82     HTON_TEMPORARY_NOT_SUPPORTED |
83     HTON_NO_PARTITION;
84 
85   /*
86     As long as the server implementation keeps using legacy_db_type,
87     as for example in mysql_truncate(),
88     we can not rely on the fact that different mysqld process will assign
89     consistently the same legacy_db_type for a given storage engine name.
90     In particular, using different --loose-skip-xxx options between
91     ./mysqld --bootstrap
92     ./mysqld
93     creates bogus .frm forms when bootstrapping the performance schema,
94     if we rely on ha_initialize_handlerton to assign a really dynamic value.
95     To fix this, a dedicated DB_TYPE is officially assigned to
96     the performance schema. See Bug#43039.
97   */
98   pfs_hton->db_type= DB_TYPE_PERFORMANCE_SCHEMA;
99 
100   PFS_engine_table_share::init_all_locks();
101 
102   DBUG_RETURN(0);
103 }
104 
pfs_done_func(void * p)105 static int pfs_done_func(void *p)
106 {
107   DBUG_ENTER("pfs_done_func");
108 
109   pfs_hton= NULL;
110 
111   PFS_engine_table_share::delete_all_locks();
112 
113   DBUG_RETURN(0);
114 }
115 
116 static struct st_mysql_show_var pfs_status_vars[]=
117 {
118   {"Performance_schema_mutex_classes_lost",
119     (char*) &mutex_class_lost, SHOW_LONG_NOFLUSH},
120   {"Performance_schema_rwlock_classes_lost",
121     (char*) &rwlock_class_lost, SHOW_LONG_NOFLUSH},
122   {"Performance_schema_cond_classes_lost",
123     (char*) &cond_class_lost, SHOW_LONG_NOFLUSH},
124   {"Performance_schema_thread_classes_lost",
125     (char*) &thread_class_lost, SHOW_LONG_NOFLUSH},
126   {"Performance_schema_file_classes_lost",
127     (char*) &file_class_lost, SHOW_LONG_NOFLUSH},
128   {"Performance_schema_mutex_instances_lost",
129     (char*) &mutex_lost, SHOW_LONG},
130   {"Performance_schema_rwlock_instances_lost",
131     (char*) &rwlock_lost, SHOW_LONG},
132   {"Performance_schema_cond_instances_lost",
133     (char*) &cond_lost, SHOW_LONG},
134   {"Performance_schema_thread_instances_lost",
135     (char*) &thread_lost, SHOW_LONG},
136   {"Performance_schema_file_instances_lost",
137     (char*) &file_lost, SHOW_LONG},
138   {"Performance_schema_file_handles_lost",
139     (char*) &file_handle_lost, SHOW_LONG},
140   {"Performance_schema_locker_lost",
141     (char*) &locker_lost, SHOW_LONG},
142   /* table shares, can be flushed */
143   {"Performance_schema_table_instances_lost",
144     (char*) &table_share_lost, SHOW_LONG},
145   /* table handles, can be flushed */
146   {"Performance_schema_table_handles_lost",
147     (char*) &table_lost, SHOW_LONG},
148   {NullS, NullS, SHOW_LONG}
149 };
150 
151 struct st_mysql_storage_engine pfs_storage_engine=
152 { MYSQL_HANDLERTON_INTERFACE_VERSION };
153 
154 const char* pfs_engine_name= "PERFORMANCE_SCHEMA";
155 
mysql_declare_plugin(perfschema)156 mysql_declare_plugin(perfschema)
157 {
158   MYSQL_STORAGE_ENGINE_PLUGIN,
159   &pfs_storage_engine,
160   pfs_engine_name,
161   "Marc Alff, Oracle", /* Formerly Sun Microsystems, formerly MySQL */
162   "Performance Schema",
163   PLUGIN_LICENSE_GPL,
164   pfs_init_func,                                /* Plugin Init */
165   pfs_done_func,                                /* Plugin Deinit */
166   0x0001 /* 0.1 */,
167   pfs_status_vars,                              /* status variables */
168   NULL,                                         /* system variables */
169   NULL,                                         /* config options */
170   0,                                            /* flags */
171 }
172 mysql_declare_plugin_end;
173 
ha_perfschema(handlerton * hton,TABLE_SHARE * share)174 ha_perfschema::ha_perfschema(handlerton *hton, TABLE_SHARE *share)
175   : handler(hton, share), m_table_share(NULL), m_table(NULL)
176 {}
177 
~ha_perfschema()178 ha_perfschema::~ha_perfschema()
179 {}
180 
181 static const char *ha_pfs_exts[]= {
182   NullS
183 };
184 
bas_ext() const185 const char **ha_perfschema::bas_ext() const
186 {
187   return ha_pfs_exts;
188 }
189 
open(const char * name,int mode,uint test_if_locked)190 int ha_perfschema::open(const char *name, int mode, uint test_if_locked)
191 {
192   DBUG_ENTER("ha_perfschema::open");
193 
194   m_table_share= find_table_share(table_share->db.str,
195                                   table_share->table_name.str);
196   if (! m_table_share)
197     DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
198 
199   thr_lock_data_init(m_table_share->m_thr_lock_ptr, &m_thr_lock, NULL);
200   ref_length= m_table_share->m_ref_length;
201 
202   psi_open();
203 
204   DBUG_RETURN(0);
205 }
206 
close(void)207 int ha_perfschema::close(void)
208 {
209   DBUG_ENTER("ha_perfschema::close");
210   m_table_share= NULL;
211   delete m_table;
212   m_table= NULL;
213 
214   psi_close();
215 
216   DBUG_RETURN(0);
217 }
218 
write_row(uchar * buf)219 int ha_perfschema::write_row(uchar *buf)
220 {
221   int result;
222 
223   DBUG_ENTER("ha_perfschema::write_row");
224 
225   ha_statistic_increment(&SSV::ha_write_count);
226   DBUG_ASSERT(m_table_share);
227 
228   if (m_table_share->m_write_row)
229     result= m_table_share->m_write_row(table, buf, table->field);
230   else
231   {
232     result= HA_ERR_WRONG_COMMAND;
233   }
234 
235   DBUG_RETURN(result);
236 }
237 
use_hidden_primary_key(void)238 void ha_perfschema::use_hidden_primary_key(void)
239 {
240   /*
241     This is also called in case of row based replication,
242     see TABLE::mark_columns_needed_for_update().
243     Add all columns to the read set, but do not touch the write set,
244     as some columns in the SETUP_ tables are not writable.
245   */
246   table->column_bitmaps_set_no_signal(&table->s->all_set, table->write_set);
247 }
248 
update_row(const uchar * old_data,uchar * new_data)249 int ha_perfschema::update_row(const uchar *old_data, uchar *new_data)
250 {
251   DBUG_ENTER("ha_perfschema::update_row");
252 
253   if (is_executed_by_slave())
254     DBUG_RETURN(0);
255 
256   DBUG_ASSERT(m_table);
257   int result= m_table->update_row(table, old_data, new_data, table->field);
258   DBUG_RETURN(result);
259 }
260 
rnd_init(bool scan)261 int ha_perfschema::rnd_init(bool scan)
262 {
263   int result;
264   DBUG_ENTER("ha_perfschema::rnd_init");
265 
266   DBUG_ASSERT(m_table_share);
267   DBUG_ASSERT(m_table_share->m_open_table != NULL);
268 
269   stats.records= 0;
270   if (m_table == NULL)
271     m_table= m_table_share->m_open_table();
272   else
273     m_table->reset_position();
274 
275   result= m_table ? 0 : HA_ERR_OUT_OF_MEM;
276   DBUG_RETURN(result);
277 }
278 
rnd_end(void)279 int ha_perfschema::rnd_end(void)
280 {
281   DBUG_ENTER("ha_perfschema::rnd_end");
282   DBUG_ASSERT(m_table);
283   delete m_table;
284   m_table= NULL;
285   DBUG_RETURN(0);
286 }
287 
rnd_next(uchar * buf)288 int ha_perfschema::rnd_next(uchar *buf)
289 {
290   DBUG_ENTER("ha_perfschema::rnd_next");
291 
292   DBUG_ASSERT(m_table);
293   int result= m_table->rnd_next();
294   if (result == 0)
295   {
296     result= m_table->read_row(table, buf, table->field);
297     if (result == 0)
298       stats.records++;
299   }
300   DBUG_RETURN(result);
301 }
302 
position(const uchar * record)303 void ha_perfschema::position(const uchar *record)
304 {
305   DBUG_ENTER("ha_perfschema::position");
306 
307   DBUG_ASSERT(m_table);
308   m_table->get_position(ref);
309   DBUG_VOID_RETURN;
310 }
311 
rnd_pos(uchar * buf,uchar * pos)312 int ha_perfschema::rnd_pos(uchar *buf, uchar *pos)
313 {
314   DBUG_ENTER("ha_perfschema::rnd_pos");
315 
316   DBUG_ASSERT(m_table);
317   int result= m_table->rnd_pos(pos);
318   if (result == 0)
319     result= m_table->read_row(table, buf, table->field);
320   DBUG_RETURN(result);
321 }
322 
info(uint flag)323 int ha_perfschema::info(uint flag)
324 {
325   DBUG_ENTER("ha_perfschema::info");
326   DBUG_ASSERT(m_table_share);
327   if (flag & HA_STATUS_VARIABLE)
328     stats.records= m_table_share->m_records;
329   if (flag & HA_STATUS_CONST)
330     ref_length= m_table_share->m_ref_length;
331   DBUG_RETURN(0);
332 }
333 
delete_all_rows(void)334 int ha_perfschema::delete_all_rows(void)
335 {
336   int result;
337 
338   DBUG_ENTER("ha_perfschema::delete_all_rows");
339 
340   if (is_executed_by_slave())
341     DBUG_RETURN(0);
342 
343   DBUG_ASSERT(m_table_share);
344   if (m_table_share->m_delete_all_rows)
345     result= m_table_share->m_delete_all_rows();
346   else
347   {
348     result= HA_ERR_WRONG_COMMAND;
349   }
350   DBUG_RETURN(result);
351 }
352 
truncate()353 int ha_perfschema::truncate()
354 {
355   return delete_all_rows();
356 }
357 
store_lock(THD * thd,THR_LOCK_DATA ** to,enum thr_lock_type lock_type)358 THR_LOCK_DATA **ha_perfschema::store_lock(THD *thd,
359                                        THR_LOCK_DATA **to,
360                                        enum thr_lock_type lock_type)
361 {
362   if (lock_type != TL_IGNORE && m_thr_lock.type == TL_UNLOCK)
363     m_thr_lock.type= lock_type;
364   *to++= &m_thr_lock;
365   m_thr_lock.m_psi= m_psi;
366   return to;
367 }
368 
delete_table(const char * name)369 int ha_perfschema::delete_table(const char *name)
370 {
371   DBUG_ENTER("ha_perfschema::delete_table");
372   DBUG_RETURN(0);
373 }
374 
rename_table(const char * from,const char * to)375 int ha_perfschema::rename_table(const char * from, const char * to)
376 {
377   DBUG_ENTER("ha_perfschema::rename_table ");
378   DBUG_RETURN(HA_ERR_WRONG_COMMAND);
379 }
380 
create(const char * name,TABLE * table_arg,HA_CREATE_INFO * create_info)381 int ha_perfschema::create(const char *name, TABLE *table_arg,
382                           HA_CREATE_INFO *create_info)
383 {
384   DBUG_ENTER("ha_perfschema::create");
385   DBUG_ASSERT(table_arg);
386   DBUG_ASSERT(table_arg->s);
387   if (find_table_share(table_arg->s->db.str,
388                        table_arg->s->table_name.str))
389   {
390     /*
391       Attempting to create a known performance schema table.
392       Allowing the create, to create .FRM files,
393       for the initial database install, and mysql_upgrade.
394       This should fail once .FRM are removed.
395     */
396     DBUG_RETURN(0);
397   }
398   /*
399     This is not a general purpose engine.
400     Failure to CREATE TABLE is the expected result.
401   */
402   DBUG_RETURN(HA_ERR_WRONG_COMMAND);
403 }
404 
print_error(int error,myf errflag)405 void ha_perfschema::print_error(int error, myf errflag)
406 {
407   switch (error)
408   {
409     case HA_ERR_TABLE_NEEDS_UPGRADE:
410       /*
411         The error message for ER_TABLE_NEEDS_UPGRADE refers to REPAIR table,
412         which does not apply to performance schema tables.
413       */
414       my_error(ER_WRONG_NATIVE_TABLE_STRUCTURE, MYF(0),
415                table_share->db.str, table_share->table_name.str);
416       break;
417     case HA_ERR_WRONG_COMMAND:
418       /*
419         The performance schema is not a general purpose storage engine,
420         some operations are not supported, by design.
421         We do not want to print "Command not supported",
422         which gives the impression that a command implementation is missing,
423         and that the failure should be considered a bug.
424         We print "Invalid performance_schema usage." instead,
425         to emphasise that the operation attempted is not meant to be legal,
426         and that the failure returned is indeed the expected result.
427       */
428       my_error(ER_WRONG_PERFSCHEMA_USAGE, MYF(0));
429       break;
430     default:
431      handler::print_error(error, errflag);
432      break;
433   }
434 }
435 
436