mariadb/storage/blackhole/ha_blackhole.cc
Alexander Barkov fd247cc21f MDEV-31340 Remove MY_COLLATION_HANDLER::strcasecmp()
This patch also fixes:
  MDEV-33050 Build-in schemas like oracle_schema are accent insensitive
  MDEV-33084 LASTVAL(t1) and LASTVAL(T1) do not work well with lower-case-table-names=0
  MDEV-33085 Tables T1 and t1 do not work well with ENGINE=CSV and lower-case-table-names=0
  MDEV-33086 SHOW OPEN TABLES IN DB1 -- is case insensitive with lower-case-table-names=0
  MDEV-33088 Cannot create triggers in the database `MYSQL`
  MDEV-33103 LOCK TABLE t1 AS t2 -- alias is not case sensitive with lower-case-table-names=0
  MDEV-33109 DROP DATABASE MYSQL -- does not drop SP with lower-case-table-names=0
  MDEV-33110 HANDLER commands are case insensitive with lower-case-table-names=0
  MDEV-33119 User is case insensitive in INFORMATION_SCHEMA.VIEWS
  MDEV-33120 System log table names are case insensitive with lower-cast-table-names=0

- Removing the virtual function strnncoll() from MY_COLLATION_HANDLER

- Adding a wrapper function CHARSET_INFO::streq(), to compare
  two strings for equality. For now it calls strnncoll() internally.
  In the future it will turn into a virtual function.

- Adding new accent sensitive case insensitive collations:
    - utf8mb4_general1400_as_ci
    - utf8mb3_general1400_as_ci
  They implement accent sensitive case insensitive comparison.
  The weight of a character is equal to the code point of its
  upper case variant. These collations use Unicode-14.0.0 casefolding data.

  The result of
     my_charset_utf8mb3_general1400_as_ci.strcoll()
  is very close to the former
     my_charset_utf8mb3_general_ci.strcasecmp()

  There is only a difference in a couple dozen rare characters, because:
    - the switch from "tolower" to "toupper" comparison, to make
      utf8mb3_general1400_as_ci closer to utf8mb3_general_ci
    - the switch from Unicode-3.0.0 to Unicode-14.0.0
  This difference should be tolarable. See the list of affected
  characters in the MDEV description.

  Note, utf8mb4_general1400_as_ci correctly handles non-BMP characters!
  Unlike utf8mb4_general_ci, it does not treat all BMP characters
  as equal.

- Adding classes representing names of the file based database objects:

    Lex_ident_db
    Lex_ident_table
    Lex_ident_trigger

  Their comparison collation depends on the underlying
  file system case sensitivity and on --lower-case-table-names
  and can be either my_charset_bin or my_charset_utf8mb3_general1400_as_ci.

- Adding classes representing names of other database objects,
  whose names have case insensitive comparison style,
  using my_charset_utf8mb3_general1400_as_ci:

  Lex_ident_column
  Lex_ident_sys_var
  Lex_ident_user_var
  Lex_ident_sp_var
  Lex_ident_ps
  Lex_ident_i_s_table
  Lex_ident_window
  Lex_ident_func
  Lex_ident_partition
  Lex_ident_with_element
  Lex_ident_rpl_filter
  Lex_ident_master_info
  Lex_ident_host
  Lex_ident_locale
  Lex_ident_plugin
  Lex_ident_engine
  Lex_ident_server
  Lex_ident_savepoint
  Lex_ident_charset
  engine_option_value::Name

- All the mentioned Lex_ident_xxx classes implement a method streq():

  if (ident1.streq(ident2))
     do_equal();

  This method works as a wrapper for CHARSET_INFO::streq().

- Changing a lot of "LEX_CSTRING name" to "Lex_ident_xxx name"
  in class members and in function/method parameters.

- Replacing all calls like
    system_charset_info->coll->strcasecmp(ident1, ident2)
  to
    ident1.streq(ident2)

- Taking advantage of the c++11 user defined literal operator
  for LEX_CSTRING (see m_strings.h) and Lex_ident_xxx (see lex_ident.h)
  data types. Use example:

  const Lex_ident_column primary_key_name= "PRIMARY"_Lex_ident_column;

  is now a shorter version of:

  const Lex_ident_column primary_key_name=
    Lex_ident_column({STRING_WITH_LEN("PRIMARY")});
2024-04-18 15:22:10 +04:00

454 lines
12 KiB
C++

/* Copyright (c) 2005, 2012, 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 as published by
the Free Software Foundation; version 2 of the License.
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 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-1335 USA */
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
#endif
#define MYSQL_SERVER 1
#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "ha_blackhole.h"
#include "sql_class.h" // THD, SYSTEM_THREAD_SLAVE_SQL
/**
Checks if the param 'thd' is pointing to slave applier thread and row based
replication is in use.
A row event will have its thd->query() == NULL except in cases where
replicate_annotate_row_events is enabled. In the later case the thd->query()
will be pointing to the query, received through replicated annotate event
from master.
@param thd pointer to a THD instance
@return TRUE if thread is slave applier and row based replication is in use
*/
static bool is_row_based_replication(THD *thd)
{
return thd->system_thread == SYSTEM_THREAD_SLAVE_SQL &&
(thd->query() == NULL || thd->variables.binlog_annotate_row_events);
}
/* Static declarations for handlerton */
static handler *blackhole_create_handler(handlerton *hton,
TABLE_SHARE *table,
MEM_ROOT *mem_root)
{
return new (mem_root) ha_blackhole(hton, table);
}
/* Static declarations for shared structures */
static mysql_mutex_t blackhole_mutex;
static HASH 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 mode, uint test_if_locked)
{
DBUG_ENTER("ha_blackhole::open");
if (!(share= get_share(name)))
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
thr_lock_data_init(&share->lock, &lock, NULL);
DBUG_RETURN(0);
}
int ha_blackhole::close(void)
{
DBUG_ENTER("ha_blackhole::close");
free_share(share);
DBUG_RETURN(0);
}
int ha_blackhole::create(const char *name, TABLE *table_arg,
HA_CREATE_INFO *create_info)
{
DBUG_ENTER("ha_blackhole::create");
DBUG_RETURN(0);
}
/*
Intended to support partitioning.
Allows a particular partition to be truncated.
*/
int ha_blackhole::truncate()
{
DBUG_ENTER("ha_blackhole::truncate");
DBUG_RETURN(0);
}
const char *ha_blackhole::index_type(uint key_number)
{
DBUG_ENTER("ha_blackhole::index_type");
DBUG_RETURN((table_share->key_info[key_number].flags & HA_FULLTEXT) ?
"FULLTEXT" :
(table_share->key_info[key_number].flags & HA_SPATIAL) ?
"SPATIAL" :
(table_share->key_info[key_number].algorithm ==
HA_KEY_ALG_RTREE) ? "RTREE" : "BTREE");
}
int ha_blackhole::write_row(const uchar * buf)
{
DBUG_ENTER("ha_blackhole::write_row");
DBUG_RETURN(table->next_number_field ? update_auto_increment() : 0);
}
int ha_blackhole::update_row(const uchar *old_data, const uchar *new_data)
{
DBUG_ENTER("ha_blackhole::update_row");
THD *thd= ha_thd();
if (is_row_based_replication(thd))
DBUG_RETURN(0);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
int ha_blackhole::delete_row(const uchar *buf)
{
DBUG_ENTER("ha_blackhole::delete_row");
THD *thd= ha_thd();
if (is_row_based_replication(thd))
DBUG_RETURN(0);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
int ha_blackhole::rnd_init(bool scan)
{
DBUG_ENTER("ha_blackhole::rnd_init");
DBUG_RETURN(0);
}
int ha_blackhole::rnd_next(uchar *buf)
{
int rc;
DBUG_ENTER("ha_blackhole::rnd_next");
THD *thd= ha_thd();
if (is_row_based_replication(thd))
rc= 0;
else
rc= HA_ERR_END_OF_FILE;
DBUG_RETURN(rc);
}
int ha_blackhole::rnd_pos(uchar * buf, uchar *pos)
{
DBUG_ENTER("ha_blackhole::rnd_pos");
DBUG_ASSERT(0);
DBUG_RETURN(0);
}
void ha_blackhole::position(const uchar *record)
{
DBUG_ENTER("ha_blackhole::position");
DBUG_ASSERT(0);
DBUG_VOID_RETURN;
}
int ha_blackhole::info(uint flag)
{
DBUG_ENTER("ha_blackhole::info");
bzero((char*) &stats, sizeof(stats));
/*
The following is required to get replication to work as otherwise
test_quick_select() will think the table is empty and thus any
update/delete will not have any rows to update.
*/
stats.records= 2;
/*
Block size should not be 0 as this will cause division by zero
in scan_time()
*/
stats.block_size= 8192;
if (flag & HA_STATUS_AUTO)
stats.auto_increment_value= 1;
DBUG_RETURN(0);
}
int ha_blackhole::external_lock(THD *thd, int lock_type)
{
DBUG_ENTER("ha_blackhole::external_lock");
DBUG_RETURN(0);
}
THR_LOCK_DATA **ha_blackhole::store_lock(THD *thd,
THR_LOCK_DATA **to,
enum thr_lock_type lock_type)
{
DBUG_ENTER("ha_blackhole::store_lock");
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)
&& !thd_tablespace_op(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;
DBUG_RETURN(to);
}
int ha_blackhole::index_read_map(uchar * buf, const uchar * key,
key_part_map keypart_map,
enum ha_rkey_function find_flag)
{
int rc;
DBUG_ENTER("ha_blackhole::index_read");
THD *thd= ha_thd();
if (is_row_based_replication(thd))
rc= 0;
else
rc= HA_ERR_END_OF_FILE;
DBUG_RETURN(rc);
}
int ha_blackhole::index_read_idx_map(uchar * buf, uint idx, const uchar * key,
key_part_map keypart_map,
enum ha_rkey_function find_flag)
{
int rc;
DBUG_ENTER("ha_blackhole::index_read_idx");
THD *thd= ha_thd();
if (is_row_based_replication(thd))
rc= 0;
else
rc= HA_ERR_END_OF_FILE;
DBUG_RETURN(rc);
}
int ha_blackhole::index_read_last_map(uchar * buf, const uchar * key,
key_part_map keypart_map)
{
int rc;
DBUG_ENTER("ha_blackhole::index_read_last");
THD *thd= ha_thd();
if (is_row_based_replication(thd))
rc= 0;
else
rc= HA_ERR_END_OF_FILE;
DBUG_RETURN(rc);
}
int ha_blackhole::index_next(uchar * buf)
{
int rc;
DBUG_ENTER("ha_blackhole::index_next");
rc= HA_ERR_END_OF_FILE;
DBUG_RETURN(rc);
}
int ha_blackhole::index_prev(uchar * buf)
{
int rc;
DBUG_ENTER("ha_blackhole::index_prev");
rc= HA_ERR_END_OF_FILE;
DBUG_RETURN(rc);
}
int ha_blackhole::index_first(uchar * buf)
{
int rc;
DBUG_ENTER("ha_blackhole::index_first");
rc= HA_ERR_END_OF_FILE;
DBUG_RETURN(rc);
}
int ha_blackhole::index_last(uchar * buf)
{
int rc;
DBUG_ENTER("ha_blackhole::index_last");
rc= HA_ERR_END_OF_FILE;
DBUG_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);
if (!(share= (st_blackhole_share*)
my_hash_search(&blackhole_open_tables,
(uchar*) table_name, length)))
{
if (!(share= (st_blackhole_share*) my_malloc(PSI_INSTRUMENT_ME,
sizeof(st_blackhole_share) + length, MYF(MY_WME | MY_ZEROFILL))))
goto error;
share->table_name_length= length;
strmov(share->table_name, table_name);
if (my_hash_insert(&blackhole_open_tables, (uchar*) share))
{
my_free(share);
share= NULL;
goto error;
}
thr_lock_init(&share->lock);
}
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)
my_hash_delete(&blackhole_open_tables, (uchar*) share);
mysql_mutex_unlock(&blackhole_mutex);
}
static void blackhole_free_key(st_blackhole_share *share)
{
thr_lock_delete(&share->lock);
my_free(share);
}
static uchar* blackhole_get_key(st_blackhole_share *share, size_t *length,
my_bool not_used __attribute__((unused)))
{
*length= share->table_name_length;
return (uchar*) share->table_name;
}
#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_GLOBAL}
};
void init_blackhole_psi_keys()
{
const char* category= "blackhole";
int count;
if (PSI_server == NULL)
return;
count= array_elements(all_blackhole_mutexes);
PSI_server->register_mutex(category, all_blackhole_mutexes, 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->db_type= DB_TYPE_BLACKHOLE_DB;
blackhole_hton->create= blackhole_create_handler;
blackhole_hton->drop_table= [](handlerton *, const char*) { return -1; };
blackhole_hton->flags= HTON_CAN_RECREATE;
mysql_mutex_init(bh_key_mutex_blackhole,
&blackhole_mutex, MY_MUTEX_INIT_FAST);
(void) my_hash_init(PSI_INSTRUMENT_ME, &blackhole_open_tables,
Lex_ident_table::charset_info(),
32, 0, 0,
(my_hash_get_key) blackhole_get_key,
(my_hash_free_key) blackhole_free_key, 0);
return 0;
}
static int blackhole_fini(void *p)
{
my_hash_free(&blackhole_open_tables);
mysql_mutex_destroy(&blackhole_mutex);
return 0;
}
struct st_mysql_storage_engine blackhole_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
maria_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 */
blackhole_fini, /* Plugin Deinit */
0x0100 /* 1.0 */,
NULL, /* status variables */
NULL, /* system variables */
"1.0", /* string version */
MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
}
maria_declare_plugin_end;