| 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 | |
| 34 | handlerton *pfs_hton= NULL; |
| 35 | |
| 36 | static 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 | |
| 43 | static 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 | |
| 50 | static const PFS_engine_table_share* |
| 51 | find_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 | |
| 63 | static 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 | |
| 74 | static 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 | |
| 80 | static 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 | |
| 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 | |
| 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 | |
| 204 | maria_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 | } |
| 220 | maria_declare_plugin_end; |
| 221 | |
| 222 | ha_perfschema::ha_perfschema(handlerton *hton, TABLE_SHARE *share) |
| 223 | : handler(hton, share), m_table_share(NULL), m_table(NULL) |
| 224 | {} |
| 225 | |
| 226 | ha_perfschema::~ha_perfschema() |
| 227 | {} |
| 228 | |
| 229 | int 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 | |
| 244 | int 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 | |
| 254 | int 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 | |
| 267 | void 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 | |
| 278 | int 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 | |
| 292 | int 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 | |
| 303 | int 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 | |
| 324 | int 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 | |
| 333 | int 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 | |
| 355 | void 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 | |
| 364 | int 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 | |
| 381 | int 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 | |
| 392 | int 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 | |
| 413 | int ha_perfschema::truncate() |
| 414 | { |
| 415 | return delete_all_rows(); |
| 416 | } |
| 417 | |
| 418 | THR_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 | |
| 429 | int ha_perfschema::delete_table(const char *name) |
| 430 | { |
| 431 | DBUG_ENTER("ha_perfschema::delete_table" ); |
| 432 | DBUG_RETURN(0); |
| 433 | } |
| 434 | |
| 435 | int 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 | |
| 441 | int 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 | |
| 452 | void 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 | |