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