mariadb/storage/perfschema/ha_perfschema.cc

501 lines
15 KiB
C++
Raw Normal View History

2019-12-10 15:35:00 +01:00
/* Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
2020-01-19 12:52:07 +01:00
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2020-01-19 12:52:07 +01:00
GNU General Public License, version 2.0, for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
/**
@file storage/perfschema/ha_perfschema.cc
Performance schema storage engine (implementation).
*/
#include "sql_plugin.h"
#include "my_pthread.h"
#include "ha_perfschema.h"
#include "pfs_engine_table.h"
#include "pfs_column_values.h"
#include "pfs_instr_class.h"
#include "pfs_instr.h"
#include "pfs_account.h"
#include "pfs_host.h"
#include "pfs_user.h"
2019-12-10 15:35:00 +01:00
#include "pfs_program.h"
#include "pfs_prepared_stmt.h"
#include "pfs_buffer_container.h"
handlerton *pfs_hton= NULL;
2019-12-10 15:35:00 +01:00
#define PFS_ENABLED() (pfs_initialized && (pfs_enabled || m_table_share->m_perpetual))
static handler* pfs_create_handler(handlerton *hton,
TABLE_SHARE *table,
MEM_ROOT *mem_root)
{
return new (mem_root) ha_perfschema(hton, table);
}
static int compare_database_names(const char *name1, const char *name2)
{
if (lower_case_table_names)
return strcasecmp(name1, name2);
return strcmp(name1, name2);
}
static const PFS_engine_table_share*
find_table_share(const char *db, const char *name)
{
DBUG_ENTER("find_table_share");
if (compare_database_names(db, PERFORMANCE_SCHEMA_str.str) != 0)
DBUG_RETURN(NULL);
const PFS_engine_table_share* result;
result= PFS_engine_table::find_engine_table_share(name);
DBUG_RETURN(result);
}
static int pfs_discover_table(handlerton *hton, THD *thd, TABLE_SHARE *share)
{
const PFS_engine_table_share *pfs_share;
if ((pfs_share= find_table_share(share->db.str, share->table_name.str)))
return share->init_from_sql_statement_string(thd, false,
pfs_share->sql.str,
pfs_share->sql.length);
return HA_ERR_NO_SUCH_TABLE;
}
static int pfs_discover_table_existence(handlerton *hton, const char *db,
const char *table_name)
{
return MY_TEST(find_table_share(db, table_name));
}
static int pfs_init_func(void *p)
{
DBUG_ENTER("pfs_init_func");
pfs_hton= reinterpret_cast<handlerton *> (p);
pfs_hton->create= pfs_create_handler;
pfs_hton->drop_table= [](handlerton *, const char*) { return 0; };
pfs_hton->show_status= pfs_show_status;
MDEV-11412 Ensure that table is truly dropped when using DROP TABLE The used code is largely based on code from Tencent The problem is that in some rare cases there may be a conflict between .frm files and the files in the storage engine. In this case the DROP TABLE was not able to properly drop the table. Some MariaDB/MySQL forks has solved this by adding a FORCE option to DROP TABLE. After some discussion among MariaDB developers, we concluded that users expects that DROP TABLE should always work, even if the table would not be consistent. There should not be a need to use a separate keyword to ensure that the table is really deleted. The used solution is: - If a .frm table doesn't exists, try dropping the table from all storage engines. - If the .frm table exists but the table does not exist in the engine try dropping the table from all storage engines. - Update storage engines using many table files (.CVS, MyISAM, Aria) to succeed with the drop even if some of the files are missing. - Add HTON_AUTOMATIC_DELETE_TABLE to handlerton's where delete_table() is not needed and always succeed. This is used by ha_delete_table_force() to know which handlers to ignore when trying to drop a table without a .frm file. The disadvantage of this solution is that a DROP TABLE on a non existing table will be a bit slower as we have to ask all active storage engines if they know anything about the table. Other things: - Added a new flag MY_IGNORE_ENOENT to my_delete() to not give an error if the file doesn't exist. This simplifies some of the code. - Don't clear thd->error in ha_delete_table() if there was an active error. This is a bug fix. - handler::delete_table() will not abort if first file doesn't exists. This is bug fix to handle the case when a drop table was aborted in the middle. - Cleaned up mysql_rm_table_no_locks() to ensure that if_exists uses same code path as when it's not used. - Use non_existing_Table_error() to detect if table didn't exists. Old code used different errors tests in different position. - Table_triggers_list::drop_all_triggers() now drops trigger file if it can't be parsed instead of leaving it hanging around (bug fix) - InnoDB doesn't anymore print error about .frm file out of sync with InnoDB directory if .frm file does not exists. This change was required to be able to try to drop an InnoDB file when .frm doesn't exists. - Fixed bug in mi_delete_table() where the .MYD file would not be dropped if the .MYI file didn't exists. - Fixed memory leak in Mroonga when deleting non existing table - Fixed memory leak in Connect when deleting non existing table Bugs fixed introduced by the original version of this commit: MDEV-22826 Presence of Spider prevents tables from being force-deleted from other engines
2020-06-01 23:27:14 +03:00
pfs_hton->flags= (HTON_ALTER_NOT_SUPPORTED |
HTON_TEMPORARY_NOT_SUPPORTED |
HTON_NO_PARTITION |
HTON_NO_BINLOG_ROW_OPT |
HTON_AUTOMATIC_DELETE_TABLE);
/*
As long as the server implementation keeps using legacy_db_type,
as for example in mysql_truncate(),
we can not rely on the fact that different mysqld process will assign
consistently the same legacy_db_type for a given storage engine name.
In particular, using different --loose-skip-xxx options between
./mysqld --bootstrap
./mysqld
creates bogus .frm forms when bootstrapping the performance schema,
if we rely on ha_initialize_handlerton to assign a really dynamic value.
To fix this, a dedicated DB_TYPE is officially assigned to
the performance schema. See Bug#43039.
*/
pfs_hton->db_type= DB_TYPE_PERFORMANCE_SCHEMA;
pfs_hton->discover_table= pfs_discover_table;
pfs_hton->discover_table_existence= pfs_discover_table_existence;
pfs_hton->discover_table_names= pfs_discover_table_names;
PFS_engine_table_share::init_all_locks();
DBUG_RETURN(0);
}
static int pfs_done_func(void *p)
{
DBUG_ENTER("pfs_done_func");
pfs_hton= NULL;
PFS_engine_table_share::delete_all_locks();
DBUG_RETURN(0);
}
2019-12-10 15:35:00 +01:00
static int show_func_mutex_instances_lost(THD *thd, SHOW_VAR *var, char *buff)
{
var->type= SHOW_LONG;
var->value= buff;
long *value= reinterpret_cast<long*>(buff);
*value= global_mutex_container.get_lost_counter();
return 0;
}
static struct st_mysql_show_var pfs_status_vars[]=
{
{"Performance_schema_mutex_classes_lost",
(char*) &mutex_class_lost, SHOW_LONG_NOFLUSH},
{"Performance_schema_rwlock_classes_lost",
(char*) &rwlock_class_lost, SHOW_LONG_NOFLUSH},
{"Performance_schema_cond_classes_lost",
(char*) &cond_class_lost, SHOW_LONG_NOFLUSH},
{"Performance_schema_thread_classes_lost",
(char*) &thread_class_lost, SHOW_LONG_NOFLUSH},
{"Performance_schema_file_classes_lost",
(char*) &file_class_lost, SHOW_LONG_NOFLUSH},
{"Performance_schema_socket_classes_lost",
(char*) &socket_class_lost, SHOW_LONG_NOFLUSH},
2019-12-10 15:35:00 +01:00
{"Performance_schema_memory_classes_lost",
(char*) &memory_class_lost, SHOW_LONG_NOFLUSH},
{"Performance_schema_mutex_instances_lost",
2019-12-10 15:35:00 +01:00
(char*) &show_func_mutex_instances_lost, SHOW_FUNC},
{"Performance_schema_rwlock_instances_lost",
2019-12-10 15:35:00 +01:00
(char*) &global_rwlock_container.m_lost, SHOW_LONG},
{"Performance_schema_cond_instances_lost",
2019-12-10 15:35:00 +01:00
(char*) &global_cond_container.m_lost, SHOW_LONG},
{"Performance_schema_thread_instances_lost",
2019-12-10 15:35:00 +01:00
(char*) &global_thread_container.m_lost, SHOW_LONG},
{"Performance_schema_file_instances_lost",
2019-12-10 15:35:00 +01:00
(char*) &global_file_container.m_lost, SHOW_LONG},
{"Performance_schema_file_handles_lost",
(char*) &file_handle_lost, SHOW_LONG},
{"Performance_schema_socket_instances_lost",
2019-12-10 15:35:00 +01:00
(char*) &global_socket_container.m_lost, SHOW_LONG},
{"Performance_schema_locker_lost",
(char*) &locker_lost, SHOW_LONG},
/* table shares, can be flushed */
{"Performance_schema_table_instances_lost",
2019-12-10 15:35:00 +01:00
(char*) &global_table_share_container.m_lost, SHOW_LONG},
/* table handles, can be flushed */
{"Performance_schema_table_handles_lost",
2019-12-10 15:35:00 +01:00
(char*) &global_table_container.m_lost, SHOW_LONG},
/* table lock stats, can be flushed */
{"Performance_schema_table_lock_stat_lost",
(char*) &global_table_share_lock_container.m_lost, SHOW_LONG},
/* table index stats, can be flushed */
{"Performance_schema_index_stat_lost",
(char*) &global_table_share_index_container.m_lost, SHOW_LONG},
{"Performance_schema_hosts_lost",
2019-12-10 15:35:00 +01:00
(char*) &global_host_container.m_lost, SHOW_LONG},
{"Performance_schema_users_lost",
2019-12-10 15:35:00 +01:00
(char*) &global_user_container.m_lost, SHOW_LONG},
{"Performance_schema_accounts_lost",
2019-12-10 15:35:00 +01:00
(char*) &global_account_container.m_lost, SHOW_LONG},
{"Performance_schema_stage_classes_lost",
(char*) &stage_class_lost, SHOW_LONG},
{"Performance_schema_statement_classes_lost",
(char*) &statement_class_lost, SHOW_LONG},
{"Performance_schema_digest_lost",
(char*) &digest_lost, SHOW_LONG},
2013-03-26 00:03:13 +02:00
{"Performance_schema_session_connect_attrs_lost",
(char*) &session_connect_attrs_lost, SHOW_LONG},
2019-12-10 15:35:00 +01:00
{"Performance_schema_program_lost",
(char*) &global_program_container.m_lost, SHOW_LONG},
{"Performance_schema_nested_statement_lost",
(char*) &nested_statement_lost, SHOW_LONG},
{"Performance_schema_prepared_statements_lost",
(char*) &global_prepared_stmt_container.m_lost, SHOW_LONG},
{"Performance_schema_metadata_lock_lost",
(char*) &global_mdl_container.m_lost, SHOW_LONG},
{NullS, NullS, SHOW_LONG}
};
struct st_mysql_storage_engine pfs_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
const char* pfs_engine_name= "PERFORMANCE_SCHEMA";
2011-04-25 17:22:25 +02:00
maria_declare_plugin(perfschema)
{
MYSQL_STORAGE_ENGINE_PLUGIN,
&pfs_storage_engine,
pfs_engine_name,
"Marc Alff, Oracle",
"Performance Schema",
PLUGIN_LICENSE_GPL,
pfs_init_func,
pfs_done_func,
0x0001,
pfs_status_vars,
NULL,
"5.6.40",
MariaDB_PLUGIN_MATURITY_STABLE
2011-04-25 17:22:25 +02:00
}
maria_declare_plugin_end;
ha_perfschema::ha_perfschema(handlerton *hton, TABLE_SHARE *share)
: handler(hton, share), m_table_share(NULL), m_table(NULL)
{}
ha_perfschema::~ha_perfschema()
{}
int ha_perfschema::open(const char *name, int mode, uint test_if_locked)
{
DBUG_ENTER("ha_perfschema::open");
m_table_share= find_table_share(table_share->db.str,
table_share->table_name.str);
if (! m_table_share)
DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
thr_lock_data_init(m_table_share->m_thr_lock_ptr, &m_thr_lock, NULL);
ref_length= m_table_share->m_ref_length;
DBUG_RETURN(0);
}
int ha_perfschema::close(void)
{
DBUG_ENTER("ha_perfschema::close");
m_table_share= NULL;
delete m_table;
m_table= NULL;
DBUG_RETURN(0);
}
int ha_perfschema::write_row(const uchar *buf)
{
int result;
DBUG_ENTER("ha_perfschema::write_row");
2019-12-10 15:35:00 +01:00
if (!PFS_ENABLED())
2013-03-26 00:03:13 +02:00
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
DBUG_ASSERT(m_table_share);
result= m_table_share->write_row(table, buf, table->field);
DBUG_RETURN(result);
}
void ha_perfschema::use_hidden_primary_key(void)
{
/*
This is also called in case of row based replication,
see TABLE::mark_columns_needed_for_update().
Add all columns to the read set, but do not touch the write set,
as some columns in the SETUP_ tables are not writable.
*/
table->column_bitmaps_set_no_signal(&table->s->all_set, table->write_set);
}
int ha_perfschema::update_row(const uchar *old_data, const uchar *new_data)
{
DBUG_ENTER("ha_perfschema::update_row");
2019-12-10 15:35:00 +01:00
if (!PFS_ENABLED())
2013-03-26 00:03:13 +02:00
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
2014-05-07 10:04:30 +02:00
if (is_executed_by_slave())
DBUG_RETURN(0);
DBUG_ASSERT(m_table);
int result= m_table->update_row(table, old_data, new_data, table->field);
DBUG_RETURN(result);
}
int ha_perfschema::delete_row(const uchar *buf)
{
DBUG_ENTER("ha_perfschema::delete_row");
2019-12-10 15:35:00 +01:00
if (!PFS_ENABLED())
2013-03-26 00:03:13 +02:00
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
DBUG_ASSERT(m_table);
int result= m_table->delete_row(table, buf, table->field);
DBUG_RETURN(result);
}
int ha_perfschema::rnd_init(bool scan)
{
int result;
DBUG_ENTER("ha_perfschema::rnd_init");
DBUG_ASSERT(m_table_share);
DBUG_ASSERT(m_table_share->m_open_table != NULL);
stats.records= 0;
if (m_table == NULL)
m_table= m_table_share->m_open_table();
else
m_table->reset_position();
if (m_table != NULL)
m_table->rnd_init(scan);
result= m_table ? 0 : HA_ERR_OUT_OF_MEM;
DBUG_RETURN(result);
}
int ha_perfschema::rnd_end(void)
{
DBUG_ENTER("ha_perfschema::rnd_end");
DBUG_ASSERT(m_table);
delete m_table;
m_table= NULL;
DBUG_RETURN(0);
}
int ha_perfschema::rnd_next(uchar *buf)
{
DBUG_ENTER("ha_perfschema::rnd_next");
2019-12-10 15:35:00 +01:00
if (!PFS_ENABLED())
2014-05-07 10:04:30 +02:00
{
table->status= STATUS_NOT_FOUND;
2013-03-26 00:03:13 +02:00
DBUG_RETURN(HA_ERR_END_OF_FILE);
2014-05-07 10:04:30 +02:00
}
DBUG_ASSERT(m_table);
int result= m_table->rnd_next();
if (result == 0)
{
result= m_table->read_row(table, buf, table->field);
if (result == 0)
stats.records++;
}
2014-05-07 10:04:30 +02:00
table->status= (result ? STATUS_NOT_FOUND : 0);
DBUG_RETURN(result);
}
void ha_perfschema::position(const uchar *record)
{
DBUG_ENTER("ha_perfschema::position");
DBUG_ASSERT(m_table);
m_table->get_position(ref);
DBUG_VOID_RETURN;
}
int ha_perfschema::rnd_pos(uchar *buf, uchar *pos)
{
DBUG_ENTER("ha_perfschema::rnd_pos");
2019-12-10 15:35:00 +01:00
if (!PFS_ENABLED())
2014-05-07 10:04:30 +02:00
{
table->status= STATUS_NOT_FOUND;
2013-03-26 00:03:13 +02:00
DBUG_RETURN(HA_ERR_END_OF_FILE);
2014-05-07 10:04:30 +02:00
}
DBUG_ASSERT(m_table);
int result= m_table->rnd_pos(pos);
if (result == 0)
result= m_table->read_row(table, buf, table->field);
2014-05-07 10:04:30 +02:00
table->status= (result ? STATUS_NOT_FOUND : 0);
DBUG_RETURN(result);
}
int ha_perfschema::info(uint flag)
{
DBUG_ENTER("ha_perfschema::info");
DBUG_ASSERT(m_table_share);
if (flag & HA_STATUS_VARIABLE)
stats.records= m_table_share->get_row_count();
if (flag & HA_STATUS_CONST)
ref_length= m_table_share->m_ref_length;
DBUG_RETURN(0);
}
int ha_perfschema::delete_all_rows(void)
{
int result;
DBUG_ENTER("ha_perfschema::delete_all_rows");
2019-12-10 15:35:00 +01:00
if (!PFS_ENABLED())
2013-03-26 00:03:13 +02:00
DBUG_RETURN(0);
2014-05-07 10:04:30 +02:00
if (is_executed_by_slave())
DBUG_RETURN(0);
DBUG_ASSERT(m_table_share);
if (m_table_share->m_delete_all_rows)
result= m_table_share->m_delete_all_rows();
else
{
result= HA_ERR_WRONG_COMMAND;
}
DBUG_RETURN(result);
}
int ha_perfschema::truncate()
{
return delete_all_rows();
}
THR_LOCK_DATA **ha_perfschema::store_lock(THD *thd,
THR_LOCK_DATA **to,
enum thr_lock_type lock_type)
{
if (lock_type != TL_IGNORE && m_thr_lock.type == TL_UNLOCK)
m_thr_lock.type= lock_type;
*to++= &m_thr_lock;
m_thr_lock.m_psi= m_psi;
return to;
}
int ha_perfschema::delete_table(const char *name)
{
DBUG_ENTER("ha_perfschema::delete_table");
DBUG_RETURN(0);
}
int ha_perfschema::rename_table(const char * from, const char * to)
{
DBUG_ENTER("ha_perfschema::rename_table ");
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
int ha_perfschema::create(const char *name, TABLE *table_arg,
HA_CREATE_INFO *create_info)
{
DBUG_ENTER("ha_perfschema::create");
/*
This is not a general purpose engine.
Failure to CREATE TABLE is the expected result.
*/
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
void ha_perfschema::print_error(int error, myf errflag)
{
switch (error)
{
case HA_ERR_TABLE_NEEDS_UPGRADE:
/*
The error message for ER_TABLE_NEEDS_UPGRADE refers to REPAIR table,
which does not apply to performance schema tables.
*/
my_error(ER_WRONG_NATIVE_TABLE_STRUCTURE, MYF(0),
table_share->db.str, table_share->table_name.str);
break;
case HA_ERR_WRONG_COMMAND:
/*
The performance schema is not a general purpose storage engine,
some operations are not supported, by design.
We do not want to print "Command not supported",
which gives the impression that a command implementation is missing,
and that the failure should be considered a bug.
We print "Invalid performance_schema usage." instead,
to emphasise that the operation attempted is not meant to be legal,
and that the failure returned is indeed the expected result.
*/
my_error(ER_WRONG_PERFSCHEMA_USAGE, MYF(0));
break;
default:
handler::print_error(error, errflag);
break;
}
}