polardbxengine/storage/blackhole/ha_blackhole.cc

357 lines
9.7 KiB
C++

/* Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
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
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, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#define MYSQL_SERVER 1
#include "storage/blackhole/ha_blackhole.h"
#include "map_helpers.h"
#include "my_dbug.h"
#include "my_psi_config.h"
#include "mysql/plugin.h"
#include "mysql/psi/mysql_memory.h"
#include "sql/sql_class.h" // THD, SYSTEM_THREAD_SLAVE_*
#include "template_utils.h"
using std::string;
using std::unique_ptr;
static PSI_memory_key bh_key_memory_blackhole_share;
static bool is_slave_applier(THD *thd) {
return thd->system_thread == SYSTEM_THREAD_SLAVE_SQL ||
thd->system_thread == SYSTEM_THREAD_SLAVE_WORKER;
}
/* Static declarations for handlerton */
static handler *blackhole_create_handler(handlerton *hton, TABLE_SHARE *table,
bool, MEM_ROOT *mem_root) {
return new (mem_root) ha_blackhole(hton, table);
}
/* Static declarations for shared structures */
struct blackhole_free_share {
void operator()(st_blackhole_share *share) const {
thr_lock_delete(&share->lock);
my_free(share);
}
};
using blackhole_share_with_deleter =
unique_ptr<st_blackhole_share, blackhole_free_share>;
static mysql_mutex_t blackhole_mutex;
static unique_ptr<collation_unordered_map<string, blackhole_share_with_deleter>>
blackhole_open_tables;
static st_blackhole_share *get_share(const char *table_name);
static void free_share(st_blackhole_share *share);
/*****************************************************************************
** BLACKHOLE tables
*****************************************************************************/
ha_blackhole::ha_blackhole(handlerton *hton, TABLE_SHARE *table_arg)
: handler(hton, table_arg) {}
int ha_blackhole::open(const char *name, int, uint, const dd::Table *) {
DBUG_TRACE;
if (!(share = get_share(name))) return HA_ERR_OUT_OF_MEM;
thr_lock_data_init(&share->lock, &lock, NULL);
return 0;
}
int ha_blackhole::close(void) {
DBUG_TRACE;
free_share(share);
return 0;
}
int ha_blackhole::create(const char *, TABLE *, HA_CREATE_INFO *, dd::Table *) {
DBUG_TRACE;
return 0;
}
int ha_blackhole::write_row(uchar *) {
DBUG_TRACE;
return table->next_number_field ? update_auto_increment() : 0;
}
int ha_blackhole::update_row(const uchar *, uchar *) {
DBUG_TRACE;
THD *thd = ha_thd();
if (is_slave_applier(thd) && thd->query().str == NULL) return 0;
return HA_ERR_WRONG_COMMAND;
}
int ha_blackhole::delete_row(const uchar *) {
DBUG_TRACE;
THD *thd = ha_thd();
if (is_slave_applier(thd) && thd->query().str == NULL) return 0;
return HA_ERR_WRONG_COMMAND;
}
int ha_blackhole::rnd_init(bool) {
DBUG_TRACE;
return 0;
}
int ha_blackhole::rnd_next(uchar *) {
int rc;
DBUG_TRACE;
THD *thd = ha_thd();
if (is_slave_applier(thd) && thd->query().str == NULL)
rc = 0;
else
rc = HA_ERR_END_OF_FILE;
return rc;
}
int ha_blackhole::rnd_pos(uchar *, uchar *) {
DBUG_TRACE;
DBUG_ASSERT(0);
return 0;
}
void ha_blackhole::position(const uchar *) {
DBUG_TRACE;
DBUG_ASSERT(0);
}
int ha_blackhole::info(uint flag) {
DBUG_TRACE;
stats = ha_statistics();
if (flag & HA_STATUS_AUTO) stats.auto_increment_value = 1;
return 0;
}
int ha_blackhole::external_lock(THD *, int) {
DBUG_TRACE;
return 0;
}
THR_LOCK_DATA **ha_blackhole::store_lock(THD *thd, THR_LOCK_DATA **to,
enum thr_lock_type lock_type) {
DBUG_TRACE;
if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) {
/*
Here is where we get into the guts of a row level lock.
If TL_UNLOCK is set
If we are not doing a LOCK TABLE or DISCARD/IMPORT
TABLESPACE, then allow multiple writers
*/
if ((lock_type >= TL_WRITE_CONCURRENT_INSERT && lock_type <= TL_WRITE) &&
!thd_in_lock_tables(thd))
lock_type = TL_WRITE_ALLOW_WRITE;
/*
In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
MySQL would use the lock TL_READ_NO_INSERT on t2, and that
would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
to t2. Convert the lock to a normal read lock to allow
concurrent inserts to t2.
*/
if (lock_type == TL_READ_NO_INSERT && !thd_in_lock_tables(thd))
lock_type = TL_READ;
lock.type = lock_type;
}
*to++ = &lock;
return to;
}
int ha_blackhole::index_read_map(uchar *, const uchar *, key_part_map,
enum ha_rkey_function) {
int rc;
DBUG_TRACE;
THD *thd = ha_thd();
if (is_slave_applier(thd) && thd->query().str == NULL)
rc = 0;
else
rc = HA_ERR_END_OF_FILE;
return rc;
}
int ha_blackhole::index_read_idx_map(uchar *, uint, const uchar *, key_part_map,
enum ha_rkey_function) {
int rc;
DBUG_TRACE;
THD *thd = ha_thd();
if (is_slave_applier(thd) && thd->query().str == NULL)
rc = 0;
else
rc = HA_ERR_END_OF_FILE;
return rc;
}
int ha_blackhole::index_read_last_map(uchar *, const uchar *, key_part_map) {
int rc;
DBUG_TRACE;
THD *thd = ha_thd();
if (is_slave_applier(thd) && thd->query().str == NULL)
rc = 0;
else
rc = HA_ERR_END_OF_FILE;
return rc;
}
int ha_blackhole::index_next(uchar *) {
int rc;
DBUG_TRACE;
rc = HA_ERR_END_OF_FILE;
return rc;
}
int ha_blackhole::index_prev(uchar *) {
int rc;
DBUG_TRACE;
rc = HA_ERR_END_OF_FILE;
return rc;
}
int ha_blackhole::index_first(uchar *) {
int rc;
DBUG_TRACE;
rc = HA_ERR_END_OF_FILE;
return rc;
}
int ha_blackhole::index_last(uchar *) {
int rc;
DBUG_TRACE;
rc = HA_ERR_END_OF_FILE;
return rc;
}
static st_blackhole_share *get_share(const char *table_name) {
st_blackhole_share *share;
uint length;
length = (uint)strlen(table_name);
mysql_mutex_lock(&blackhole_mutex);
auto it = blackhole_open_tables->find(table_name);
if (it == blackhole_open_tables->end()) {
if (!(share = (st_blackhole_share *)my_malloc(
bh_key_memory_blackhole_share,
sizeof(st_blackhole_share) + length, MYF(MY_WME | MY_ZEROFILL))))
goto error;
share->table_name_length = length;
my_stpcpy(share->table_name, table_name);
blackhole_open_tables->emplace(table_name,
blackhole_share_with_deleter(share));
thr_lock_init(&share->lock);
} else
share = it->second.get();
share->use_count++;
error:
mysql_mutex_unlock(&blackhole_mutex);
return share;
}
static void free_share(st_blackhole_share *share) {
mysql_mutex_lock(&blackhole_mutex);
if (!--share->use_count) blackhole_open_tables->erase(share->table_name);
mysql_mutex_unlock(&blackhole_mutex);
}
#ifdef HAVE_PSI_INTERFACE
static PSI_mutex_key bh_key_mutex_blackhole;
static PSI_mutex_info all_blackhole_mutexes[] = {
{&bh_key_mutex_blackhole, "blackhole", PSI_FLAG_SINGLETON, 0,
PSI_DOCUMENT_ME}};
static PSI_memory_info all_blackhole_memory[] = {
{&bh_key_memory_blackhole_share, "blackhole_share",
PSI_FLAG_ONLY_GLOBAL_STAT, 0, PSI_DOCUMENT_ME}};
static void init_blackhole_psi_keys() {
const char *category = "blackhole";
int count;
count = static_cast<int>(array_elements(all_blackhole_mutexes));
mysql_mutex_register(category, all_blackhole_mutexes, count);
count = static_cast<int>(array_elements(all_blackhole_memory));
mysql_memory_register(category, all_blackhole_memory, count);
}
#endif
static int blackhole_init(void *p) {
handlerton *blackhole_hton;
#ifdef HAVE_PSI_INTERFACE
init_blackhole_psi_keys();
#endif
blackhole_hton = (handlerton *)p;
blackhole_hton->state = SHOW_OPTION_YES;
blackhole_hton->db_type = DB_TYPE_BLACKHOLE_DB;
blackhole_hton->create = blackhole_create_handler;
blackhole_hton->flags = HTON_CAN_RECREATE;
mysql_mutex_init(bh_key_mutex_blackhole, &blackhole_mutex,
MY_MUTEX_INIT_FAST);
blackhole_open_tables.reset(
new collation_unordered_map<string, blackhole_share_with_deleter>(
system_charset_info, bh_key_memory_blackhole_share));
return 0;
}
static int blackhole_fini(void *) {
blackhole_open_tables.reset();
mysql_mutex_destroy(&blackhole_mutex);
return 0;
}
struct st_mysql_storage_engine blackhole_storage_engine = {
MYSQL_HANDLERTON_INTERFACE_VERSION};
mysql_declare_plugin(blackhole){
MYSQL_STORAGE_ENGINE_PLUGIN,
&blackhole_storage_engine,
"BLACKHOLE",
"MySQL AB",
"/dev/null storage engine (anything you write to it disappears)",
PLUGIN_LICENSE_GPL,
blackhole_init, /* Plugin Init */
NULL, /* Plugin check uninstall */
blackhole_fini, /* Plugin Deinit */
0x0100 /* 1.0 */,
NULL, /* status variables */
NULL, /* system variables */
NULL, /* config options */
0, /* flags */
} mysql_declare_plugin_end;