mariadb/storage/tokudb/tokudb_information_schema.cc

1216 lines
37 KiB
C++
Raw Normal View History

2015-12-15 17:23:58 +01:00
/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
/* -*- mode: C; c-basic-offset: 4 -*- */
#ident "$Id$"
/*======
This file is part of TokuDB
Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
TokuDBis is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2,
as published by the Free Software Foundation.
TokuDB 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
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TokuDB. If not, see <http://www.gnu.org/licenses/>.
======= */
#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
#include "hatoku_hton.h"
#include "tokudb_information_schema.h"
#include "sql_time.h"
#include "tokudb_background.h"
namespace tokudb {
namespace information_schema {
static void field_store_time_t(Field* field, time_t time) {
MYSQL_TIME my_time;
struct tm tm_time;
if (time) {
localtime_r(&time, &tm_time);
localtime_to_TIME(&my_time, &tm_time);
my_time.time_type = MYSQL_TIMESTAMP_DATETIME;
#ifdef MARIA_PLUGIN_INTERFACE_VERSION
field->store_time(&my_time);
#else
2015-12-15 17:23:58 +01:00
field->store_time(&my_time, MYSQL_TIMESTAMP_DATETIME);
#endif
2015-12-15 17:23:58 +01:00
field->set_notnull();
} else {
field->set_null();
}
}
st_mysql_information_schema trx_information_schema = {
MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
};
ST_FIELD_INFO trx_field_info[] = {
{"trx_id", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
{"trx_mysql_thread_id", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
{"trx_time", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
{NULL, 0, MYSQL_TYPE_NULL, 0, 0, NULL, SKIP_OPEN_TABLE}
};
struct trx_extra_t {
THD *thd;
TABLE *table;
};
int trx_callback(
DB_TXN* txn,
iterate_row_locks_callback iterate_locks,
void* locks_extra,
void *extra) {
uint64_t txn_id = txn->id64(txn);
fix bogus stalls in the lock tree for low concurrency applications Merge into the MariaDB tree the pull request from Rich Prohaska for PerconaFT. These changes are needed to get parallel replication to work with TokuDB. Once the pull request is accepted by Percona and the new upstream version enters MariaDB, this commit can be superseded. Original commit message from Rich Prohaska: 1. Fix the release before wait race The release before wait race occurs when a lock is released by transaction A after transaction B tried to acquire it but before transaction B has a chance to register it's pending lock request. There are several ways to fix this problem, but we want to optimize for the common situation of minimal lock conflicts, which is what the lock acquisition algorithm currently does. Our solution to the release before wait race is for transaction B to retry its lock request after its lock request has been added to the pending lock set. 2. Fix the retry race The retry race occurs in the current lock retry algorithm which assumes that if some transaction is running lock retry, then my transaction does not also need to run it. There is a chance that some pending lock requests will be skipped, but these lock requests will eventually time out. For applications with small numbers of concurrent transactions, timeouts will frequently occur, and the application throughput will be very small. The solution to the retry race is to use a group retry algorithm. All threads run through the retry logic. Sequence numbers are used to group retries into batches such that one transaction can run the retry logic on behalf of several transactions. This amortizes the retry cost. The sequence numbers also ensure that when a transaction releases its locks, all of the pending lock requests that it is blocking are retried. 3. Implement a mechanism to find and kill a pending lock request Tags lock requests with a client id, use the client id as a key into the pending lock requests sets to find a lock request, complete the lock request with a lock timeout error. Copyright (c) 2016, Rich Prohaska All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2016-11-23 12:29:38 +01:00
uint64_t client_id;
void *client_extra;
txn->get_client_id(txn, &client_id, &client_extra);
2015-12-15 17:23:58 +01:00
uint64_t start_time = txn->get_start_time(txn);
trx_extra_t* e = reinterpret_cast<struct trx_extra_t*>(extra);
THD* thd = e->thd;
TABLE* table = e->table;
table->field[0]->store(txn_id, false);
table->field[1]->store(client_id, false);
uint64_t tnow = (uint64_t) ::time(NULL);
table->field[2]->store(tnow >= start_time ? tnow - start_time : 0, false);
int error = schema_table_store_record(thd, table);
if (!error && thd_kill_level(thd))
2015-12-15 17:23:58 +01:00
error = ER_QUERY_INTERRUPTED;
return error;
}
#if MYSQL_VERSION_ID >= 50600
int trx_fill_table(THD* thd, TABLE_LIST* tables, Item* cond) {
#else
int trx_fill_table(THD* thd, TABLE_LIST* tables, COND* cond) {
#endif
TOKUDB_DBUG_ENTER("");
int error;
tokudb_hton_initialized_lock.lock_read();
if (!tokudb_hton_initialized) {
error = ER_PLUGIN_IS_NOT_LOADED;
my_error(error, MYF(0), tokudb_hton_name);
} else {
trx_extra_t e = { thd, tables->table };
error = db_env->iterate_live_transactions(db_env, trx_callback, &e);
if (error)
my_error(ER_GET_ERRNO, MYF(0), error, tokudb_hton_name);
}
tokudb_hton_initialized_lock.unlock();
TOKUDB_DBUG_RETURN(error);
}
int trx_init(void* p) {
ST_SCHEMA_TABLE *schema = (ST_SCHEMA_TABLE*) p;
schema->fields_info = trx_field_info;
schema->fill_table = trx_fill_table;
return 0;
}
int trx_done(void* p) {
return 0;
}
st_mysql_plugin trx = {
MYSQL_INFORMATION_SCHEMA_PLUGIN,
&trx_information_schema,
"TokuDB_trx",
"Percona",
"Percona TokuDB Storage Engine with Fractal Tree(tm) Technology",
PLUGIN_LICENSE_GPL,
trx_init, /* plugin init */
trx_done, /* plugin deinit */
TOKUDB_PLUGIN_VERSION,
NULL, /* status variables */
NULL, /* system variables */
#ifdef MARIA_PLUGIN_INTERFACE_VERSION
tokudb::sysvars::version,
MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
#else
NULL, /* config options */
0, /* flags */
#endif
};
st_mysql_information_schema lock_waits_information_schema = {
MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
};
ST_FIELD_INFO lock_waits_field_info[] = {
{"requesting_trx_id", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
{"blocking_trx_id", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
{"lock_waits_dname", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"lock_waits_key_left", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"lock_waits_key_right", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"lock_waits_start_time", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
{"lock_waits_table_schema", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"lock_waits_table_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"lock_waits_table_dictionary_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{NULL, 0, MYSQL_TYPE_NULL, 0, 0, NULL, SKIP_OPEN_TABLE}
};
struct lock_waits_extra_t {
THD* thd;
TABLE* table;
};
int lock_waits_callback(
DB* db,
uint64_t requesting_txnid,
const DBT* left_key,
const DBT* right_key,
uint64_t blocking_txnid,
uint64_t start_time,
void *extra) {
lock_waits_extra_t* e =
reinterpret_cast<struct lock_waits_extra_t*>(extra);
THD* thd = e->thd;
TABLE* table = e->table;
table->field[0]->store(requesting_txnid, false);
table->field[1]->store(blocking_txnid, false);
const char* dname = tokudb_get_index_name(db);
size_t dname_length = strlen(dname);
table->field[2]->store(dname, dname_length, system_charset_info);
String left_str;
tokudb_pretty_left_key(db, left_key, &left_str);
table->field[3]->store(
left_str.ptr(),
left_str.length(),
system_charset_info);
String right_str;
tokudb_pretty_right_key(db, right_key, &right_str);
table->field[4]->store(
right_str.ptr(),
right_str.length(),
system_charset_info);
table->field[5]->store(start_time, false);
String database_name, table_name, dictionary_name;
tokudb_split_dname(dname, database_name, table_name, dictionary_name);
table->field[6]->store(
database_name.c_ptr(),
database_name.length(),
system_charset_info);
table->field[7]->store(
table_name.c_ptr(),
table_name.length(),
system_charset_info);
table->field[8]->store(
dictionary_name.c_ptr(),
dictionary_name.length(),
system_charset_info);
int error = schema_table_store_record(thd, table);
if (!error && thd_kill_level(thd))
2015-12-15 17:23:58 +01:00
error = ER_QUERY_INTERRUPTED;
return error;
}
#if MYSQL_VERSION_ID >= 50600
int lock_waits_fill_table(THD* thd, TABLE_LIST* tables, Item* cond) {
#else
int lock_waits_fill_table(THD* thd, TABLE_LIST* tables, COND* cond) {
#endif
TOKUDB_DBUG_ENTER("");
int error;
tokudb_hton_initialized_lock.lock_read();
if (!tokudb_hton_initialized) {
error = ER_PLUGIN_IS_NOT_LOADED;
my_error(error, MYF(0), tokudb_hton_name);
} else {
lock_waits_extra_t e = { thd, tables->table };
error = db_env->iterate_pending_lock_requests(
db_env,
lock_waits_callback,
&e);
if (error)
my_error(ER_GET_ERRNO, MYF(0), error, tokudb_hton_name);
}
tokudb_hton_initialized_lock.unlock();
TOKUDB_DBUG_RETURN(error);
}
int lock_waits_init(void* p) {
ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*)p;
schema->fields_info = lock_waits_field_info;
schema->fill_table = lock_waits_fill_table;
return 0;
}
int lock_waits_done(void *p) {
return 0;
}
st_mysql_plugin lock_waits = {
MYSQL_INFORMATION_SCHEMA_PLUGIN,
&lock_waits_information_schema,
"TokuDB_lock_waits",
"Percona",
"Percona TokuDB Storage Engine with Fractal Tree(tm) Technology",
PLUGIN_LICENSE_GPL,
lock_waits_init, /* plugin init */
lock_waits_done, /* plugin deinit */
TOKUDB_PLUGIN_VERSION,
NULL, /* status variables */
NULL, /* system variables */
#ifdef MARIA_PLUGIN_INTERFACE_VERSION
tokudb::sysvars::version,
MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
#else
NULL, /* config options */
0, /* flags */
#endif
};
st_mysql_information_schema locks_information_schema = {
MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
};
ST_FIELD_INFO locks_field_info[] = {
{"locks_trx_id", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
{"locks_mysql_thread_id", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
{"locks_dname", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"locks_key_left", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"locks_key_right", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"locks_table_schema", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"locks_table_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"locks_table_dictionary_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{NULL, 0, MYSQL_TYPE_NULL, 0, 0, NULL, SKIP_OPEN_TABLE}
};
struct locks_extra_t {
THD* thd;
TABLE* table;
};
int locks_callback(
DB_TXN* txn,
iterate_row_locks_callback iterate_locks,
void* locks_extra,
void* extra) {
uint64_t txn_id = txn->id64(txn);
fix bogus stalls in the lock tree for low concurrency applications Merge into the MariaDB tree the pull request from Rich Prohaska for PerconaFT. These changes are needed to get parallel replication to work with TokuDB. Once the pull request is accepted by Percona and the new upstream version enters MariaDB, this commit can be superseded. Original commit message from Rich Prohaska: 1. Fix the release before wait race The release before wait race occurs when a lock is released by transaction A after transaction B tried to acquire it but before transaction B has a chance to register it's pending lock request. There are several ways to fix this problem, but we want to optimize for the common situation of minimal lock conflicts, which is what the lock acquisition algorithm currently does. Our solution to the release before wait race is for transaction B to retry its lock request after its lock request has been added to the pending lock set. 2. Fix the retry race The retry race occurs in the current lock retry algorithm which assumes that if some transaction is running lock retry, then my transaction does not also need to run it. There is a chance that some pending lock requests will be skipped, but these lock requests will eventually time out. For applications with small numbers of concurrent transactions, timeouts will frequently occur, and the application throughput will be very small. The solution to the retry race is to use a group retry algorithm. All threads run through the retry logic. Sequence numbers are used to group retries into batches such that one transaction can run the retry logic on behalf of several transactions. This amortizes the retry cost. The sequence numbers also ensure that when a transaction releases its locks, all of the pending lock requests that it is blocking are retried. 3. Implement a mechanism to find and kill a pending lock request Tags lock requests with a client id, use the client id as a key into the pending lock requests sets to find a lock request, complete the lock request with a lock timeout error. Copyright (c) 2016, Rich Prohaska All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2016-11-23 12:29:38 +01:00
uint64_t client_id;
void *client_extra;
txn->get_client_id(txn, &client_id, &client_extra);
2015-12-15 17:23:58 +01:00
locks_extra_t* e = reinterpret_cast<struct locks_extra_t*>(extra);
THD* thd = e->thd;
TABLE* table = e->table;
int error = 0;
DB* db;
DBT left_key, right_key;
while (error == 0 &&
iterate_locks(&db, &left_key, &right_key, locks_extra) == 0) {
table->field[0]->store(txn_id, false);
table->field[1]->store(client_id, false);
const char* dname = tokudb_get_index_name(db);
size_t dname_length = strlen(dname);
table->field[2]->store(dname, dname_length, system_charset_info);
String left_str;
tokudb_pretty_left_key(db, &left_key, &left_str);
table->field[3]->store(
left_str.ptr(),
left_str.length(),
system_charset_info);
String right_str;
tokudb_pretty_right_key(db, &right_key, &right_str);
table->field[4]->store(
right_str.ptr(),
right_str.length(),
system_charset_info);
String database_name, table_name, dictionary_name;
tokudb_split_dname(dname, database_name, table_name, dictionary_name);
table->field[5]->store(
database_name.c_ptr(),
database_name.length(),
system_charset_info);
table->field[6]->store(
table_name.c_ptr(),
table_name.length(),
system_charset_info);
table->field[7]->store(
dictionary_name.c_ptr(),
dictionary_name.length(),
system_charset_info);
error = schema_table_store_record(thd, table);
if (!error && thd_kill_level(thd))
2015-12-15 17:23:58 +01:00
error = ER_QUERY_INTERRUPTED;
}
return error;
}
#if MYSQL_VERSION_ID >= 50600
int locks_fill_table(THD* thd, TABLE_LIST* tables, Item* cond) {
#else
int locks_fill_table(THD* thd, TABLE_LIST* tables, COND* cond) {
#endif
TOKUDB_DBUG_ENTER("");
int error;
tokudb_hton_initialized_lock.lock_read();
if (!tokudb_hton_initialized) {
error = ER_PLUGIN_IS_NOT_LOADED;
my_error(error, MYF(0), tokudb_hton_name);
} else {
locks_extra_t e = { thd, tables->table };
error = db_env->iterate_live_transactions(db_env, locks_callback, &e);
if (error)
my_error(ER_GET_ERRNO, MYF(0), error, tokudb_hton_name);
}
tokudb_hton_initialized_lock.unlock();
TOKUDB_DBUG_RETURN(error);
}
int locks_init(void* p) {
ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*)p;
schema->fields_info = locks_field_info;
schema->fill_table = locks_fill_table;
return 0;
}
int locks_done(void* p) {
return 0;
}
st_mysql_plugin locks = {
MYSQL_INFORMATION_SCHEMA_PLUGIN,
&locks_information_schema,
"TokuDB_locks",
"Percona",
"Percona TokuDB Storage Engine with Fractal Tree(tm) Technology",
PLUGIN_LICENSE_GPL,
locks_init, /* plugin init */
locks_done, /* plugin deinit */
TOKUDB_PLUGIN_VERSION,
NULL, /* status variables */
NULL, /* system variables */
#ifdef MARIA_PLUGIN_INTERFACE_VERSION
tokudb::sysvars::version,
MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
#else
NULL, /* config options */
0, /* flags */
#endif
};
st_mysql_information_schema file_map_information_schema = {
MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
};
ST_FIELD_INFO file_map_field_info[] = {
{"dictionary_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"internal_file_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"table_schema", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"table_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"table_dictionary_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{NULL, 0, MYSQL_TYPE_NULL, 0, 0, NULL, SKIP_OPEN_TABLE}
};
int report_file_map(TABLE* table, THD* thd) {
int error;
DB_TXN* txn = NULL;
DBC* tmp_cursor = NULL;
DBT curr_key;
DBT curr_val;
memset(&curr_key, 0, sizeof curr_key);
memset(&curr_val, 0, sizeof curr_val);
error = txn_begin(db_env, 0, &txn, DB_READ_UNCOMMITTED, thd);
if (error) {
goto cleanup;
}
error = db_env->get_cursor_for_directory(db_env, txn, &tmp_cursor);
if (error) {
goto cleanup;
}
while (error == 0) {
error = tmp_cursor->c_get(tmp_cursor, &curr_key, &curr_val, DB_NEXT);
if (!error) {
// We store the NULL terminator in the directory so it's included
// in the size.
// See #5789
// Recalculate and check just to be safe.
const char *dname = (const char *) curr_key.data;
size_t dname_len = strlen(dname);
assert(dname_len == curr_key.size - 1);
table->field[0]->store(dname, dname_len, system_charset_info);
const char *iname = (const char *) curr_val.data;
size_t iname_len = strlen(iname);
assert(iname_len == curr_val.size - 1);
table->field[1]->store(iname, iname_len, system_charset_info);
// split the dname
String database_name, table_name, dictionary_name;
tokudb_split_dname(
dname,
database_name,
table_name,
dictionary_name);
table->field[2]->store(
database_name.c_ptr(),
database_name.length(),
system_charset_info);
table->field[3]->store(
table_name.c_ptr(),
table_name.length(),
system_charset_info);
table->field[4]->store(
dictionary_name.c_ptr(),
dictionary_name.length(),
system_charset_info);
error = schema_table_store_record(thd, table);
}
if (!error && thd_kill_level(thd))
2015-12-15 17:23:58 +01:00
error = ER_QUERY_INTERRUPTED;
}
if (error == DB_NOTFOUND) {
error = 0;
}
cleanup:
if (tmp_cursor) {
int r = tmp_cursor->c_close(tmp_cursor);
assert(r == 0);
}
if (txn) {
commit_txn(txn, 0);
}
return error;
}
#if MYSQL_VERSION_ID >= 50600
int file_map_fill_table(THD* thd, TABLE_LIST* tables, Item* cond) {
#else
int file_map_fill_table(THD* thd, TABLE_LIST* tables, COND* cond) {
#endif
TOKUDB_DBUG_ENTER("");
int error;
TABLE* table = tables->table;
tokudb_hton_initialized_lock.lock_read();
if (!tokudb_hton_initialized) {
error = ER_PLUGIN_IS_NOT_LOADED;
my_error(error, MYF(0), tokudb_hton_name);
} else {
error = report_file_map(table, thd);
if (error)
my_error(ER_GET_ERRNO, MYF(0), error, tokudb_hton_name);
}
tokudb_hton_initialized_lock.unlock();
TOKUDB_DBUG_RETURN(error);
}
int file_map_init(void* p) {
ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*)p;
schema->fields_info = file_map_field_info;
schema->fill_table = file_map_fill_table;
return 0;
}
int file_map_done(void* p) {
return 0;
}
st_mysql_plugin file_map = {
MYSQL_INFORMATION_SCHEMA_PLUGIN,
&file_map_information_schema,
"TokuDB_file_map",
"Percona",
"Percona TokuDB Storage Engine with Fractal Tree(tm) Technology",
PLUGIN_LICENSE_GPL,
file_map_init, /* plugin init */
file_map_done, /* plugin deinit */
TOKUDB_PLUGIN_VERSION,
NULL, /* status variables */
NULL, /* system variables */
#ifdef MARIA_PLUGIN_INTERFACE_VERSION
tokudb::sysvars::version,
MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
#else
NULL, /* config options */
0, /* flags */
#endif
};
st_mysql_information_schema fractal_tree_info_information_schema = {
MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
};
ST_FIELD_INFO fractal_tree_info_field_info[] = {
{"dictionary_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"internal_file_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"bt_num_blocks_allocated", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
{"bt_num_blocks_in_use", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
{"bt_size_allocated", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
{"bt_size_in_use", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
{"table_schema", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"table_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"table_dictionary_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{NULL, 0, MYSQL_TYPE_NULL, 0, 0, NULL, SKIP_OPEN_TABLE}
};
int report_fractal_tree_info_for_db(
const DBT* dname,
const DBT* iname,
TABLE* table,
THD* thd) {
int error;
uint64_t bt_num_blocks_allocated;
uint64_t bt_num_blocks_in_use;
uint64_t bt_size_allocated;
uint64_t bt_size_in_use;
DB *db = NULL;
error = db_create(&db, db_env, 0);
if (error) {
goto exit;
}
error = db->open(db, NULL, (char *)dname->data, NULL, DB_BTREE, 0, 0666);
if (error) {
goto exit;
}
error = db->get_fractal_tree_info64(
db,
&bt_num_blocks_allocated,
&bt_num_blocks_in_use,
&bt_size_allocated,
&bt_size_in_use);
if (error) {
goto exit;
}
// We store the NULL terminator in the directory so it's included in the
// size.
// See #5789
// Recalculate and check just to be safe.
{
size_t dname_len = strlen((const char*)dname->data);
assert(dname_len == dname->size - 1);
table->field[0]->store(
(char*)dname->data,
dname_len,
system_charset_info);
size_t iname_len = strlen((const char*)iname->data);
assert(iname_len == iname->size - 1);
table->field[1]->store(
(char*)iname->data,
iname_len,
system_charset_info);
}
table->field[2]->store(bt_num_blocks_allocated, false);
table->field[3]->store(bt_num_blocks_in_use, false);
table->field[4]->store(bt_size_allocated, false);
table->field[5]->store(bt_size_in_use, false);
// split the dname
{
String database_name, table_name, dictionary_name;
tokudb_split_dname(
(const char*)dname->data,
database_name,
table_name,
dictionary_name);
table->field[6]->store(
database_name.c_ptr(),
database_name.length(),
system_charset_info);
table->field[7]->store(
table_name.c_ptr(),
table_name.length(),
system_charset_info);
table->field[8]->store(
dictionary_name.c_ptr(),
dictionary_name.length(),
system_charset_info);
}
error = schema_table_store_record(thd, table);
exit:
if (db) {
int close_error = db->close(db, 0);
if (error == 0)
error = close_error;
}
return error;
}
int report_fractal_tree_info(TABLE* table, THD* thd) {
int error;
DB_TXN* txn = NULL;
DBC* tmp_cursor = NULL;
DBT curr_key;
DBT curr_val;
memset(&curr_key, 0, sizeof curr_key);
memset(&curr_val, 0, sizeof curr_val);
error = txn_begin(db_env, 0, &txn, DB_READ_UNCOMMITTED, thd);
if (error) {
goto cleanup;
}
error = db_env->get_cursor_for_directory(db_env, txn, &tmp_cursor);
if (error) {
goto cleanup;
}
while (error == 0) {
error = tmp_cursor->c_get(tmp_cursor, &curr_key, &curr_val, DB_NEXT);
if (!error) {
error = report_fractal_tree_info_for_db(
&curr_key,
&curr_val,
table,
thd);
if (error)
error = 0; // ignore read uncommitted errors
}
if (!error && thd_kill_level(thd))
2015-12-15 17:23:58 +01:00
error = ER_QUERY_INTERRUPTED;
}
if (error == DB_NOTFOUND) {
error = 0;
}
cleanup:
if (tmp_cursor) {
int r = tmp_cursor->c_close(tmp_cursor);
assert(r == 0);
}
if (txn) {
commit_txn(txn, 0);
}
return error;
}
#if MYSQL_VERSION_ID >= 50600
int fractal_tree_info_fill_table(THD* thd, TABLE_LIST* tables, Item* cond) {
#else
int fractal_tree_info_fill_table(THD* thd, TABLE_LIST* tables, COND* cond) {
#endif
TOKUDB_DBUG_ENTER("");
int error;
TABLE* table = tables->table;
// 3938: Get a read lock on the status flag, since we must
// read it before safely proceeding
tokudb_hton_initialized_lock.lock_read();
if (!tokudb_hton_initialized) {
error = ER_PLUGIN_IS_NOT_LOADED;
my_error(error, MYF(0), tokudb_hton_name);
} else {
error = report_fractal_tree_info(table, thd);
if (error)
my_error(ER_GET_ERRNO, MYF(0), error, tokudb_hton_name);
}
//3938: unlock the status flag lock
tokudb_hton_initialized_lock.unlock();
TOKUDB_DBUG_RETURN(error);
}
int fractal_tree_info_init(void* p) {
ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*)p;
schema->fields_info = fractal_tree_info_field_info;
schema->fill_table = fractal_tree_info_fill_table;
return 0;
}
int fractal_tree_info_done(void* p) {
return 0;
}
st_mysql_plugin fractal_tree_info = {
MYSQL_INFORMATION_SCHEMA_PLUGIN,
&fractal_tree_info_information_schema,
"TokuDB_fractal_tree_info",
"Percona",
"Percona TokuDB Storage Engine with Fractal Tree(tm) Technology",
PLUGIN_LICENSE_GPL,
fractal_tree_info_init, /* plugin init */
fractal_tree_info_done, /* plugin deinit */
TOKUDB_PLUGIN_VERSION,
NULL, /* status variables */
NULL, /* system variables */
#ifdef MARIA_PLUGIN_INTERFACE_VERSION
tokudb::sysvars::version,
MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
#else
NULL, /* config options */
0, /* flags */
#endif
};
st_mysql_information_schema fractal_tree_block_map_information_schema = {
MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
};
ST_FIELD_INFO fractal_tree_block_map_field_info[] = {
{"dictionary_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"internal_file_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"checkpoint_count", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
{"blocknum", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
{"offset", 0, MYSQL_TYPE_LONGLONG, 0, MY_I_S_MAYBE_NULL, NULL, SKIP_OPEN_TABLE },
{"size", 0, MYSQL_TYPE_LONGLONG, 0, MY_I_S_MAYBE_NULL, NULL, SKIP_OPEN_TABLE },
{"table_schema", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"table_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"table_dictionary_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{NULL, 0, MYSQL_TYPE_NULL, 0, 0, NULL, SKIP_OPEN_TABLE}
};
struct report_fractal_tree_block_map_iterator_extra_t {
int64_t num_rows;
int64_t i;
uint64_t* checkpoint_counts;
int64_t* blocknums;
int64_t* diskoffs;
int64_t* sizes;
};
// This iterator is called while holding the blocktable lock.
// We should be as quick as possible.
// We don't want to do one call to get the number of rows, release the
// blocktable lock, and then do another call to get all the rows because
// the number of rows may change if we don't hold the lock.
// As a compromise, we'll do some mallocs inside the lock on the first call,
// but everything else should be fast.
int report_fractal_tree_block_map_iterator(
uint64_t checkpoint_count,
int64_t num_rows,
int64_t blocknum,
int64_t diskoff,
int64_t size,
void* iter_extra) {
struct report_fractal_tree_block_map_iterator_extra_t* e =
static_cast<struct report_fractal_tree_block_map_iterator_extra_t*>(iter_extra);
assert(num_rows > 0);
if (e->num_rows == 0) {
e->checkpoint_counts =
(uint64_t*)tokudb::memory::malloc(
num_rows * (sizeof *e->checkpoint_counts),
MYF(MY_WME|MY_ZEROFILL|MY_FAE));
e->blocknums =
(int64_t*)tokudb::memory::malloc(
num_rows * (sizeof *e->blocknums),
MYF(MY_WME|MY_ZEROFILL|MY_FAE));
e->diskoffs =
(int64_t*)tokudb::memory::malloc(
num_rows * (sizeof *e->diskoffs),
MYF(MY_WME|MY_ZEROFILL|MY_FAE));
e->sizes =
(int64_t*)tokudb::memory::malloc(
num_rows * (sizeof *e->sizes),
MYF(MY_WME|MY_ZEROFILL|MY_FAE));
e->num_rows = num_rows;
}
e->checkpoint_counts[e->i] = checkpoint_count;
e->blocknums[e->i] = blocknum;
e->diskoffs[e->i] = diskoff;
e->sizes[e->i] = size;
++(e->i);
return 0;
}
int report_fractal_tree_block_map_for_db(
const DBT* dname,
const DBT* iname,
TABLE* table,
THD* thd) {
int error;
DB* db;
// avoid struct initializers so that we can compile with older gcc versions
report_fractal_tree_block_map_iterator_extra_t e = {};
error = db_create(&db, db_env, 0);
if (error) {
goto exit;
}
error = db->open(db, NULL, (char *)dname->data, NULL, DB_BTREE, 0, 0666);
if (error) {
goto exit;
}
error = db->iterate_fractal_tree_block_map(
db,
report_fractal_tree_block_map_iterator,
&e);
{
int close_error = db->close(db, 0);
if (!error) {
error = close_error;
}
}
if (error) {
goto exit;
}
// If not, we should have gotten an error and skipped this section of code
assert(e.i == e.num_rows);
for (int64_t i = 0; error == 0 && i < e.num_rows; ++i) {
// We store the NULL terminator in the directory so it's included in the size.
// See #5789
// Recalculate and check just to be safe.
size_t dname_len = strlen((const char*)dname->data);
assert(dname_len == dname->size - 1);
table->field[0]->store(
(char*)dname->data,
dname_len,
system_charset_info);
size_t iname_len = strlen((const char*)iname->data);
assert(iname_len == iname->size - 1);
table->field[1]->store(
(char*)iname->data,
iname_len,
system_charset_info);
table->field[2]->store(e.checkpoint_counts[i], false);
table->field[3]->store(e.blocknums[i], false);
static const int64_t freelist_null = -1;
static const int64_t diskoff_unused = -2;
if (e.diskoffs[i] == diskoff_unused || e.diskoffs[i] == freelist_null) {
table->field[4]->set_null();
} else {
table->field[4]->set_notnull();
table->field[4]->store(e.diskoffs[i], false);
}
static const int64_t size_is_free = -1;
if (e.sizes[i] == size_is_free) {
table->field[5]->set_null();
} else {
table->field[5]->set_notnull();
table->field[5]->store(e.sizes[i], false);
}
// split the dname
String database_name, table_name, dictionary_name;
tokudb_split_dname(
(const char*)dname->data,
database_name,
table_name,
dictionary_name);
table->field[6]->store(
database_name.c_ptr(),
database_name.length(),
system_charset_info);
table->field[7]->store(
table_name.c_ptr(),
table_name.length(),
system_charset_info);
table->field[8]->store(
dictionary_name.c_ptr(),
dictionary_name.length(),
system_charset_info);
error = schema_table_store_record(thd, table);
}
exit:
if (e.checkpoint_counts != NULL) {
tokudb::memory::free(e.checkpoint_counts);
e.checkpoint_counts = NULL;
}
if (e.blocknums != NULL) {
tokudb::memory::free(e.blocknums);
e.blocknums = NULL;
}
if (e.diskoffs != NULL) {
tokudb::memory::free(e.diskoffs);
e.diskoffs = NULL;
}
if (e.sizes != NULL) {
tokudb::memory::free(e.sizes);
e.sizes = NULL;
}
return error;
}
int report_fractal_tree_block_map(TABLE* table, THD* thd) {
int error;
DB_TXN* txn = NULL;
DBC* tmp_cursor = NULL;
DBT curr_key;
DBT curr_val;
memset(&curr_key, 0, sizeof curr_key);
memset(&curr_val, 0, sizeof curr_val);
error = txn_begin(db_env, 0, &txn, DB_READ_UNCOMMITTED, thd);
if (error) {
goto cleanup;
}
error = db_env->get_cursor_for_directory(db_env, txn, &tmp_cursor);
if (error) {
goto cleanup;
}
while (error == 0) {
error = tmp_cursor->c_get(tmp_cursor, &curr_key, &curr_val, DB_NEXT);
if (!error) {
error = report_fractal_tree_block_map_for_db(
&curr_key,
&curr_val,
table,
thd);
}
if (!error && thd_kill_level(thd))
2015-12-15 17:23:58 +01:00
error = ER_QUERY_INTERRUPTED;
}
if (error == DB_NOTFOUND) {
error = 0;
}
cleanup:
if (tmp_cursor) {
int r = tmp_cursor->c_close(tmp_cursor);
assert(r == 0);
}
if (txn) {
commit_txn(txn, 0);
}
return error;
}
#if MYSQL_VERSION_ID >= 50600
int fractal_tree_block_map_fill_table(
THD* thd,
TABLE_LIST* tables,
Item* cond) {
#else
int fractal_tree_block_map_fill_table(
THD* thd,
TABLE_LIST* tables,
COND* cond) {
#endif
TOKUDB_DBUG_ENTER("");
int error;
TABLE* table = tables->table;
// 3938: Get a read lock on the status flag, since we must
// read it before safely proceeding
tokudb_hton_initialized_lock.lock_read();
if (!tokudb_hton_initialized) {
error = ER_PLUGIN_IS_NOT_LOADED;
my_error(error, MYF(0), tokudb_hton_name);
} else {
error = report_fractal_tree_block_map(table, thd);
if (error)
my_error(ER_GET_ERRNO, MYF(0), error, tokudb_hton_name);
}
//3938: unlock the status flag lock
tokudb_hton_initialized_lock.unlock();
TOKUDB_DBUG_RETURN(error);
}
int fractal_tree_block_map_init(void* p) {
ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*)p;
schema->fields_info = fractal_tree_block_map_field_info;
schema->fill_table = fractal_tree_block_map_fill_table;
return 0;
}
int fractal_tree_block_map_done(void *p) {
return 0;
}
st_mysql_plugin fractal_tree_block_map = {
MYSQL_INFORMATION_SCHEMA_PLUGIN,
&fractal_tree_block_map_information_schema,
"TokuDB_fractal_tree_block_map",
"Percona",
"Percona TokuDB Storage Engine with Fractal Tree(tm) Technology",
PLUGIN_LICENSE_GPL,
fractal_tree_block_map_init, /* plugin init */
fractal_tree_block_map_done, /* plugin deinit */
TOKUDB_PLUGIN_VERSION,
NULL, /* status variables */
NULL, /* system variables */
#ifdef MARIA_PLUGIN_INTERFACE_VERSION
tokudb::sysvars::version,
MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
#else
NULL, /* config options */
0, /* flags */
#endif
};
st_mysql_information_schema background_job_status_information_schema = {
MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
};
ST_FIELD_INFO background_job_status_field_info[] = {
{"id", 0, MYSQL_TYPE_LONGLONG, 0, 0, NULL, SKIP_OPEN_TABLE },
{"database_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"table_name", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"job_type", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"job_params", 256, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"scheduler", 32, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE },
{"scheduled_time", 0, MYSQL_TYPE_DATETIME, 0, 0, NULL, SKIP_OPEN_TABLE },
{"started_time", 0, MYSQL_TYPE_DATETIME, 0, MY_I_S_MAYBE_NULL, NULL, SKIP_OPEN_TABLE },
2016-09-27 17:59:58 +02:00
{"status", 1024, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, SKIP_OPEN_TABLE },
2015-12-15 17:23:58 +01:00
{NULL, 0, MYSQL_TYPE_NULL, 0, 0, NULL, SKIP_OPEN_TABLE}
};
struct background_job_status_extra {
THD* thd;
TABLE* table;
};
void background_job_status_callback(
2016-09-27 17:59:58 +02:00
tokudb::background::job_manager_t::job_t* job,
2015-12-15 17:23:58 +01:00
void* extra) {
background_job_status_extra* e =
reinterpret_cast<background_job_status_extra*>(extra);
THD* thd = e->thd;
TABLE* table = e->table;
2016-09-27 17:59:58 +02:00
const char* tmp = NULL;
2015-12-15 17:23:58 +01:00
2016-09-27 17:59:58 +02:00
table->field[0]->store(job->id(), false);
tmp = job->database();
table->field[1]->store(tmp, strlen(tmp), system_charset_info);
tmp = job->table();
table->field[2]->store(tmp, strlen(tmp), system_charset_info);
tmp = job->type();
table->field[3]->store(tmp, strlen(tmp), system_charset_info);
tmp = job->parameters();
table->field[4]->store(tmp, strlen(tmp), system_charset_info);
if (job->user_scheduled())
2016-04-26 20:58:29 +02:00
table->field[5]->store("USER", strlen("USER"), system_charset_info);
2015-12-15 17:23:58 +01:00
else
2016-04-26 20:58:29 +02:00
table->field[5]->store("AUTO", strlen("AUTO"), system_charset_info);
2015-12-15 17:23:58 +01:00
2016-09-27 17:59:58 +02:00
field_store_time_t(table->field[6], job->scheduled_time());
field_store_time_t(table->field[7], job->started_time());
tmp = job->status();
if (tmp && tmp[0] != '\0') {
table->field[8]->store(tmp, strlen(tmp), system_charset_info);
2015-12-15 17:23:58 +01:00
table->field[8]->set_notnull();
} else {
table->field[8]->store(NULL, 0, system_charset_info);
table->field[8]->set_null();
}
schema_table_store_record(thd, table);
}
int report_background_job_status(TABLE *table, THD *thd) {
int error = 0;
background_job_status_extra extra = {
thd,
table
};
tokudb::background::_job_manager->iterate_jobs(
background_job_status_callback,
&extra);
return error;
}
#if MYSQL_VERSION_ID >= 50600
int background_job_status_fill_table(THD *thd, TABLE_LIST *tables, Item *cond) {
#else
int background_job_status_fill_table(THD *thd, TABLE_LIST *tables, COND *cond) {
#endif
TOKUDB_DBUG_ENTER("");
int error;
TABLE* table = tables->table;
tokudb_hton_initialized_lock.lock_read();
if (!tokudb_hton_initialized) {
error = ER_PLUGIN_IS_NOT_LOADED;
my_error(error, MYF(0), tokudb_hton_name);
} else {
error = report_background_job_status(table, thd);
if (error)
my_error(ER_GET_ERRNO, MYF(0), error, tokudb_hton_name);
}
tokudb_hton_initialized_lock.unlock();
TOKUDB_DBUG_RETURN(error);
}
int background_job_status_init(void* p) {
ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*)p;
schema->fields_info = background_job_status_field_info;
schema->fill_table = background_job_status_fill_table;
return 0;
}
int background_job_status_done(void* p) {
return 0;
}
st_mysql_plugin background_job_status = {
MYSQL_INFORMATION_SCHEMA_PLUGIN,
&background_job_status_information_schema,
"TokuDB_background_job_status",
"Percona",
"Percona TokuDB Storage Engine with Fractal Tree(tm) Technology",
PLUGIN_LICENSE_GPL,
background_job_status_init, /* plugin init */
background_job_status_done, /* plugin deinit */
TOKUDB_PLUGIN_VERSION,
NULL, /* status variables */
NULL, /* system variables */
#ifdef MARIA_PLUGIN_INTERFACE_VERSION
tokudb::sysvars::version,
MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
#else
NULL, /* config options */
0, /* flags */
#endif
};
} // namespace information_schema
} // namespace tokudb