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